PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Filter/Inflector.php

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