3 * Zend Framework (http://framework.zend.com/)
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
10 namespace Zend\Filter
;
13 use Zend\Stdlib\ArrayUtils
;
16 * Filter chain for string inflection
18 class Inflector
extends AbstractFilter
21 * @var FilterPluginManager
23 protected $pluginManager = null;
28 protected $target = null;
33 protected $throwTargetExceptionsOn = true;
38 protected $targetReplacementIdentifier = ':';
43 protected $rules = array();
48 * @param string|array|Traversable $options Options to set
50 public function __construct($options = null)
52 if ($options instanceof Traversable
) {
53 $options = ArrayUtils
::iteratorToArray($options);
55 if (!is_array($options)) {
56 $options = func_get_args();
59 if (!empty($options)) {
60 $temp['target'] = array_shift($options);
63 if (!empty($options)) {
64 $temp['rules'] = array_shift($options);
67 if (!empty($options)) {
68 $temp['throwTargetExceptionsOn'] = array_shift($options);
71 if (!empty($options)) {
72 $temp['targetReplacementIdentifier'] = array_shift($options);
78 $this->setOptions($options);
82 * Retrieve plugin manager
84 * @return FilterPluginManager
86 public function getPluginManager()
88 if (!$this->pluginManager
instanceof FilterPluginManager
) {
89 $this->setPluginManager(new FilterPluginManager());
92 return $this->pluginManager
;
98 * @param FilterPluginManager $manager
101 public function setPluginManager(FilterPluginManager
$manager)
103 $this->pluginManager
= $manager;
110 * @param array|Traversable $options
113 public function setOptions($options)
115 if ($options instanceof Traversable
) {
116 $options = ArrayUtils
::iteratorToArray($options);
119 // Set plugin manager
120 if (array_key_exists('pluginManager', $options)) {
121 if (is_scalar($options['pluginManager']) && class_exists($options['pluginManager'])) {
122 $options['pluginManager'] = new $options['pluginManager'];
124 $this->setPluginManager($options['pluginManager']);
127 if (array_key_exists('throwTargetExceptionsOn', $options)) {
128 $this->setThrowTargetExceptionsOn($options['throwTargetExceptionsOn']);
131 if (array_key_exists('targetReplacementIdentifier', $options)) {
132 $this->setTargetReplacementIdentifier($options['targetReplacementIdentifier']);
135 if (array_key_exists('target', $options)) {
136 $this->setTarget($options['target']);
139 if (array_key_exists('rules', $options)) {
140 $this->addRules($options['rules']);
147 * Set Whether or not the inflector should throw an exception when a replacement
148 * identifier is still found within an inflected target.
150 * @param bool $throwTargetExceptionsOn
153 public function setThrowTargetExceptionsOn($throwTargetExceptionsOn)
155 $this->throwTargetExceptionsOn
= ($throwTargetExceptionsOn == true) ?
true : false;
160 * Will exceptions be thrown?
164 public function isThrowTargetExceptionsOn()
166 return $this->throwTargetExceptionsOn
;
170 * Set the Target Replacement Identifier, by default ':'
172 * @param string $targetReplacementIdentifier
175 public function setTargetReplacementIdentifier($targetReplacementIdentifier)
177 if ($targetReplacementIdentifier) {
178 $this->targetReplacementIdentifier
= (string) $targetReplacementIdentifier;
185 * Get Target Replacement Identifier
189 public function getTargetReplacementIdentifier()
191 return $this->targetReplacementIdentifier
;
196 * ex: 'scripts/:controller/:action.:suffix'
198 * @param string $target
201 public function setTarget($target)
203 $this->target
= (string) $target;
212 public function getTarget()
214 return $this->target
;
218 * Set Target Reference
220 * @param reference $target
223 public function setTargetReference(&$target)
225 $this->target
=& $target;
230 * Is the same as calling addRules() with the exception that it
231 * clears the rules before adding them.
233 * @param array $rules
236 public function setRules(array $rules)
239 $this->addRules($rules);
244 * Multi-call to setting filter rules.
246 * If prefixed with a ":" (colon), a filter rule will be added. If not
247 * prefixed, a static replacement will be added.
251 * ':controller' => array('CamelCaseToUnderscore', 'StringToLower'),
252 * ':action' => array('CamelCaseToUnderscore', 'StringToLower'),
253 * 'suffix' => 'phtml'
256 * @param array $rules
259 public function addRules(array $rules)
261 $keys = array_keys($rules);
262 foreach ($keys as $spec) {
263 if ($spec[0] == ':') {
264 $this->addFilterRule($spec, $rules[$spec]);
266 $this->setStaticRule($spec, $rules[$spec]);
276 * By default, returns all rules. If a $spec is provided, will return those
277 * rules if found, false otherwise.
279 * @param string $spec
280 * @return array|false
282 public function getRules($spec = null)
284 if (null !== $spec) {
285 $spec = $this->_normalizeSpec($spec);
286 if (isset($this->rules
[$spec])) {
287 return $this->rules
[$spec];
296 * Returns a rule set by setFilterRule(), a numeric index must be provided
298 * @param string $spec
300 * @return FilterInterface|false
302 public function getRule($spec, $index)
304 $spec = $this->_normalizeSpec($spec);
305 if (isset($this->rules
[$spec]) && is_array($this->rules
[$spec])) {
306 if (isset($this->rules
[$spec][$index])) {
307 return $this->rules
[$spec][$index];
314 * Clears the rules currently in the inflector
318 public function clearRules()
320 $this->rules
= array();
325 * Set a filtering rule for a spec. $ruleSet can be a string, Filter object
326 * or an array of strings or filter objects.
328 * @param string $spec
329 * @param array|string|\Zend\Filter\FilterInterface $ruleSet
332 public function setFilterRule($spec, $ruleSet)
334 $spec = $this->_normalizeSpec($spec);
335 $this->rules
[$spec] = array();
336 return $this->addFilterRule($spec, $ruleSet);
340 * Add a filter rule for a spec
343 * @param mixed $ruleSet
346 public function addFilterRule($spec, $ruleSet)
348 $spec = $this->_normalizeSpec($spec);
349 if (!isset($this->rules
[$spec])) {
350 $this->rules
[$spec] = array();
353 if (!is_array($ruleSet)) {
354 $ruleSet = array($ruleSet);
357 if (is_string($this->rules
[$spec])) {
358 $temp = $this->rules
[$spec];
359 $this->rules
[$spec] = array();
360 $this->rules
[$spec][] = $temp;
363 foreach ($ruleSet as $rule) {
364 $this->rules
[$spec][] = $this->_getRule($rule);
371 * Set a static rule for a spec. This is a single string value
373 * @param string $name
374 * @param string $value
377 public function setStaticRule($name, $value)
379 $name = $this->_normalizeSpec($name);
380 $this->rules
[$name] = (string) $value;
385 * Set Static Rule Reference.
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
391 * @param string $name
392 * @param mixed $reference
395 public function setStaticRuleReference($name, &$reference)
397 $name = $this->_normalizeSpec($name);
398 $this->rules
[$name] =& $reference;
405 * @param string|array $source
406 * @throws Exception\RuntimeException
409 public function filter($source)
412 foreach ((array) $source as $sourceName => $sourceValue) {
413 $source[ltrim($sourceName, ':')] = $sourceValue;
416 $pregQuotedTargetReplacementIdentifier = preg_quote($this->targetReplacementIdentifier
, '#');
417 $processedParts = array();
419 foreach ($this->rules
as $ruleName => $ruleValue) {
420 if (isset($source[$ruleName])) {
421 if (is_string($ruleValue)) {
422 // overriding the set rule
423 $processedParts['#' . $pregQuotedTargetReplacementIdentifier . $ruleName . '#'] = str_replace('\\', '\\\\', $source[$ruleName]);
424 } elseif (is_array($ruleValue)) {
425 $processedPart = $source[$ruleName];
426 foreach ($ruleValue as $ruleFilter) {
427 $processedPart = $ruleFilter($processedPart);
429 $processedParts['#' . $pregQuotedTargetReplacementIdentifier . $ruleName . '#'] = str_replace('\\', '\\\\', $processedPart);
431 } elseif (is_string($ruleValue)) {
432 $processedParts['#' . $pregQuotedTargetReplacementIdentifier . $ruleName . '#'] = str_replace('\\', '\\\\', $ruleValue);
436 // all of the values of processedParts would have been str_replace('\\', '\\\\', ..)'d to disable preg_replace backreferences
437 $inflectedTarget = preg_replace(array_keys($processedParts), array_values($processedParts), $this->target
);
439 if ($this->throwTargetExceptionsOn
&& (preg_match('#(?=' . $pregQuotedTargetReplacementIdentifier.'[A-Za-z]{1})#', $inflectedTarget) == true)) {
440 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);
443 return $inflectedTarget;
447 * Normalize spec string
449 * @param string $spec
452 protected function _normalizeSpec($spec)
454 return ltrim((string) $spec, ':&');
458 * Resolve named filters and convert them to filter objects.
460 * @param string $rule
461 * @return FilterInterface
463 protected function _getRule($rule)
465 if ($rule instanceof FilterInterface
) {
469 $rule = (string) $rule;
470 return $this->getPluginManager()->get($rule);