PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Filter/Inflector.php

https://bitbucket.org/saifshuvo/zf2
PHP | 472 lines | 228 code | 56 blank | 188 comment | 34 complexity | 565ad6be2fb2d781b54df0a4cdcd3725 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Filter;
  10. use Traversable;
  11. use Zend\Stdlib\ArrayUtils;
  12. /**
  13. * Filter chain for string inflection
  14. */
  15. class Inflector extends AbstractFilter
  16. {
  17. /**
  18. * @var FilterPluginManager
  19. */
  20. protected $pluginManager = null;
  21. /**
  22. * @var string
  23. */
  24. protected $target = null;
  25. /**
  26. * @var bool
  27. */
  28. protected $throwTargetExceptionsOn = true;
  29. /**
  30. * @var string
  31. */
  32. protected $targetReplacementIdentifier = ':';
  33. /**
  34. * @var array
  35. */
  36. protected $rules = array();
  37. /**
  38. * Constructor
  39. *
  40. * @param string|array|Traversable $options Options to set
  41. */
  42. public function __construct($options = null)
  43. {
  44. if ($options instanceof Traversable) {
  45. $options = ArrayUtils::iteratorToArray($options);
  46. }
  47. if (!is_array($options)) {
  48. $options = func_get_args();
  49. $temp = array();
  50. if (!empty($options)) {
  51. $temp['target'] = array_shift($options);
  52. }
  53. if (!empty($options)) {
  54. $temp['rules'] = array_shift($options);
  55. }
  56. if (!empty($options)) {
  57. $temp['throwTargetExceptionsOn'] = array_shift($options);
  58. }
  59. if (!empty($options)) {
  60. $temp['targetReplacementIdentifier'] = array_shift($options);
  61. }
  62. $options = $temp;
  63. }
  64. $this->setOptions($options);
  65. }
  66. /**
  67. * Retrieve plugin manager
  68. *
  69. * @return FilterPluginManager
  70. */
  71. public function getPluginManager()
  72. {
  73. if (!$this->pluginManager instanceof FilterPluginManager) {
  74. $this->setPluginManager(new FilterPluginManager());
  75. }
  76. return $this->pluginManager;
  77. }
  78. /**
  79. * Set plugin manager
  80. *
  81. * @param FilterPluginManager $manager
  82. * @return Inflector
  83. */
  84. public function setPluginManager(FilterPluginManager $manager)
  85. {
  86. $this->pluginManager = $manager;
  87. return $this;
  88. }
  89. /**
  90. * Set options
  91. *
  92. * @param array|Traversable $options
  93. * @return Inflector
  94. */
  95. public function setOptions($options)
  96. {
  97. if ($options instanceof Traversable) {
  98. $options = ArrayUtils::iteratorToArray($options);
  99. }
  100. // Set plugin manager
  101. if (array_key_exists('pluginManager', $options)) {
  102. if (is_scalar($options['pluginManager']) && class_exists($options['pluginManager'])) {
  103. $options['pluginManager'] = new $options['pluginManager'];
  104. }
  105. $this->setPluginManager($options['pluginManager']);
  106. }
  107. if (array_key_exists('throwTargetExceptionsOn', $options)) {
  108. $this->setThrowTargetExceptionsOn($options['throwTargetExceptionsOn']);
  109. }
  110. if (array_key_exists('targetReplacementIdentifier', $options)) {
  111. $this->setTargetReplacementIdentifier($options['targetReplacementIdentifier']);
  112. }
  113. if (array_key_exists('target', $options)) {
  114. $this->setTarget($options['target']);
  115. }
  116. if (array_key_exists('rules', $options)) {
  117. $this->addRules($options['rules']);
  118. }
  119. return $this;
  120. }
  121. /**
  122. * Set Whether or not the inflector should throw an exception when a replacement
  123. * identifier is still found within an inflected target.
  124. *
  125. * @param bool $throwTargetExceptionsOn
  126. * @return Inflector
  127. */
  128. public function setThrowTargetExceptionsOn($throwTargetExceptionsOn)
  129. {
  130. $this->throwTargetExceptionsOn = ($throwTargetExceptionsOn == true) ? true : false;
  131. return $this;
  132. }
  133. /**
  134. * Will exceptions be thrown?
  135. *
  136. * @return bool
  137. */
  138. public function isThrowTargetExceptionsOn()
  139. {
  140. return $this->throwTargetExceptionsOn;
  141. }
  142. /**
  143. * Set the Target Replacement Identifier, by default ':'
  144. *
  145. * @param string $targetReplacementIdentifier
  146. * @return Inflector
  147. */
  148. public function setTargetReplacementIdentifier($targetReplacementIdentifier)
  149. {
  150. if ($targetReplacementIdentifier) {
  151. $this->targetReplacementIdentifier = (string) $targetReplacementIdentifier;
  152. }
  153. return $this;
  154. }
  155. /**
  156. * Get Target Replacement Identifier
  157. *
  158. * @return string
  159. */
  160. public function getTargetReplacementIdentifier()
  161. {
  162. return $this->targetReplacementIdentifier;
  163. }
  164. /**
  165. * Set a Target
  166. * ex: 'scripts/:controller/:action.:suffix'
  167. *
  168. * @param string
  169. * @return Inflector
  170. */
  171. public function setTarget($target)
  172. {
  173. $this->target = (string) $target;
  174. return $this;
  175. }
  176. /**
  177. * Retrieve target
  178. *
  179. * @return string
  180. */
  181. public function getTarget()
  182. {
  183. return $this->target;
  184. }
  185. /**
  186. * Set Target Reference
  187. *
  188. * @param reference $target
  189. * @return Inflector
  190. */
  191. public function setTargetReference(&$target)
  192. {
  193. $this->target =& $target;
  194. return $this;
  195. }
  196. /**
  197. * SetRules() is the same as calling addRules() with the exception that it
  198. * clears the rules before adding them.
  199. *
  200. * @param array $rules
  201. * @return Inflector
  202. */
  203. public function setRules(Array $rules)
  204. {
  205. $this->clearRules();
  206. $this->addRules($rules);
  207. return $this;
  208. }
  209. /**
  210. * AddRules(): multi-call to setting filter rules.
  211. *
  212. * If prefixed with a ":" (colon), a filter rule will be added. If not
  213. * prefixed, a static replacement will be added.
  214. *
  215. * ex:
  216. * array(
  217. * ':controller' => array('CamelCaseToUnderscore', 'StringToLower'),
  218. * ':action' => array('CamelCaseToUnderscore', 'StringToLower'),
  219. * 'suffix' => 'phtml'
  220. * );
  221. *
  222. * @param array
  223. * @return Inflector
  224. */
  225. public function addRules(Array $rules)
  226. {
  227. $keys = array_keys($rules);
  228. foreach ($keys as $spec) {
  229. if ($spec[0] == ':') {
  230. $this->addFilterRule($spec, $rules[$spec]);
  231. } else {
  232. $this->setStaticRule($spec, $rules[$spec]);
  233. }
  234. }
  235. return $this;
  236. }
  237. /**
  238. * Get rules
  239. *
  240. * By default, returns all rules. If a $spec is provided, will return those
  241. * rules if found, false otherwise.
  242. *
  243. * @param string $spec
  244. * @return array|false
  245. */
  246. public function getRules($spec = null)
  247. {
  248. if (null !== $spec) {
  249. $spec = $this->_normalizeSpec($spec);
  250. if (isset($this->rules[$spec])) {
  251. return $this->rules[$spec];
  252. }
  253. return false;
  254. }
  255. return $this->rules;
  256. }
  257. /**
  258. * getRule() returns a rule set by setFilterRule(), a numeric index must be provided
  259. *
  260. * @param string $spec
  261. * @param int $index
  262. * @return FilterInterface|false
  263. */
  264. public function getRule($spec, $index)
  265. {
  266. $spec = $this->_normalizeSpec($spec);
  267. if (isset($this->rules[$spec]) && is_array($this->rules[$spec])) {
  268. if (isset($this->rules[$spec][$index])) {
  269. return $this->rules[$spec][$index];
  270. }
  271. }
  272. return false;
  273. }
  274. /**
  275. * ClearRules() clears the rules currently in the inflector
  276. *
  277. * @return Inflector
  278. */
  279. public function clearRules()
  280. {
  281. $this->rules = array();
  282. return $this;
  283. }
  284. /**
  285. * Set a filtering rule for a spec. $ruleSet can be a string, Filter object
  286. * or an array of strings or filter objects.
  287. *
  288. * @param string $spec
  289. * @param array|string|\Zend\Filter\FilterInterface $ruleSet
  290. * @return Inflector
  291. */
  292. public function setFilterRule($spec, $ruleSet)
  293. {
  294. $spec = $this->_normalizeSpec($spec);
  295. $this->rules[$spec] = array();
  296. return $this->addFilterRule($spec, $ruleSet);
  297. }
  298. /**
  299. * Add a filter rule for a spec
  300. *
  301. * @param mixed $spec
  302. * @param mixed $ruleSet
  303. * @return Inflector
  304. */
  305. public function addFilterRule($spec, $ruleSet)
  306. {
  307. $spec = $this->_normalizeSpec($spec);
  308. if (!isset($this->rules[$spec])) {
  309. $this->rules[$spec] = array();
  310. }
  311. if (!is_array($ruleSet)) {
  312. $ruleSet = array($ruleSet);
  313. }
  314. if (is_string($this->rules[$spec])) {
  315. $temp = $this->rules[$spec];
  316. $this->rules[$spec] = array();
  317. $this->rules[$spec][] = $temp;
  318. }
  319. foreach ($ruleSet as $rule) {
  320. $this->rules[$spec][] = $this->_getRule($rule);
  321. }
  322. return $this;
  323. }
  324. /**
  325. * Set a static rule for a spec. This is a single string value
  326. *
  327. * @param string $name
  328. * @param string $value
  329. * @return Inflector
  330. */
  331. public function setStaticRule($name, $value)
  332. {
  333. $name = $this->_normalizeSpec($name);
  334. $this->rules[$name] = (string) $value;
  335. return $this;
  336. }
  337. /**
  338. * Set Static Rule Reference.
  339. *
  340. * This allows a consuming class to pass a property or variable
  341. * in to be referenced when its time to build the output string from the
  342. * target.
  343. *
  344. * @param string $name
  345. * @param mixed $reference
  346. * @return Inflector
  347. */
  348. public function setStaticRuleReference($name, &$reference)
  349. {
  350. $name = $this->_normalizeSpec($name);
  351. $this->rules[$name] =& $reference;
  352. return $this;
  353. }
  354. /**
  355. * Inflect
  356. *
  357. * @param string|array $source
  358. * @throws Exception\RuntimeException
  359. * @return string
  360. */
  361. public function filter($source)
  362. {
  363. // clean source
  364. foreach ((array) $source as $sourceName => $sourceValue) {
  365. $source[ltrim($sourceName, ':')] = $sourceValue;
  366. }
  367. $pregQuotedTargetReplacementIdentifier = preg_quote($this->targetReplacementIdentifier, '#');
  368. $processedParts = array();
  369. foreach ($this->rules as $ruleName => $ruleValue) {
  370. if (isset($source[$ruleName])) {
  371. if (is_string($ruleValue)) {
  372. // overriding the set rule
  373. $processedParts['#' . $pregQuotedTargetReplacementIdentifier . $ruleName . '#'] = str_replace('\\', '\\\\', $source[$ruleName]);
  374. } elseif (is_array($ruleValue)) {
  375. $processedPart = $source[$ruleName];
  376. foreach ($ruleValue as $ruleFilter) {
  377. $processedPart = $ruleFilter($processedPart);
  378. }
  379. $processedParts['#' . $pregQuotedTargetReplacementIdentifier . $ruleName . '#'] = str_replace('\\', '\\\\', $processedPart);
  380. }
  381. } elseif (is_string($ruleValue)) {
  382. $processedParts['#' . $pregQuotedTargetReplacementIdentifier . $ruleName . '#'] = str_replace('\\', '\\\\', $ruleValue);
  383. }
  384. }
  385. // all of the values of processedParts would have been str_replace('\\', '\\\\', ..)'d to disable preg_replace backreferences
  386. $inflectedTarget = preg_replace(array_keys($processedParts), array_values($processedParts), $this->target);
  387. if ($this->throwTargetExceptionsOn && (preg_match('#(?=' . $pregQuotedTargetReplacementIdentifier.'[A-Za-z]{1})#', $inflectedTarget) == true)) {
  388. throw new Exception\RuntimeException('A replacement identifier ' . $this->targetReplacementIdentifier . ' was found inside the inflected target, perhaps a rule was not satisfied with a target source? Unsatisfied inflected target: ' . $inflectedTarget);
  389. }
  390. return $inflectedTarget;
  391. }
  392. /**
  393. * Normalize spec string
  394. *
  395. * @param string $spec
  396. * @return string
  397. */
  398. protected function _normalizeSpec($spec)
  399. {
  400. return ltrim((string) $spec, ':&');
  401. }
  402. /**
  403. * Resolve named filters and convert them to filter objects.
  404. *
  405. * @param string $rule
  406. * @return FilterInterface
  407. */
  408. protected function _getRule($rule)
  409. {
  410. if ($rule instanceof FilterInterface) {
  411. return $rule;
  412. }
  413. $rule = (string) $rule;
  414. return $this->getPluginManager()->get($rule);
  415. }
  416. }