1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-stdlib for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-stdlib/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-stdlib/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Stdlib; 10 11use Traversable; 12 13use function array_shift; 14use function is_array; 15use function is_callable; 16use function method_exists; 17use function preg_replace_callback; 18use function sprintf; 19use function str_replace; 20use function strtolower; 21use function ucwords; 22 23abstract class AbstractOptions implements ParameterObjectInterface 24{ 25 // @codingStandardsIgnoreStart 26 /** 27 * We use the __ prefix to avoid collisions with properties in 28 * user-implementations. 29 * 30 * @var bool 31 */ 32 protected $__strictMode__ = true; 33 // @codingStandardsIgnoreEnd 34 35 /** 36 * Constructor 37 * 38 * @param array|Traversable|null $options 39 */ 40 public function __construct($options = null) 41 { 42 if (null !== $options) { 43 $this->setFromArray($options); 44 } 45 } 46 47 /** 48 * Set one or more configuration properties 49 * 50 * @param array|Traversable|AbstractOptions $options 51 * @throws Exception\InvalidArgumentException 52 * @return AbstractOptions Provides fluent interface 53 */ 54 public function setFromArray($options) 55 { 56 if ($options instanceof self) { 57 $options = $options->toArray(); 58 } 59 60 if (! is_array($options) && ! $options instanceof Traversable) { 61 throw new Exception\InvalidArgumentException( 62 sprintf( 63 'Parameter provided to %s must be an %s, %s or %s', 64 __METHOD__, 65 'array', 66 'Traversable', 67 'Laminas\Stdlib\AbstractOptions' 68 ) 69 ); 70 } 71 72 foreach ($options as $key => $value) { 73 $this->__set($key, $value); 74 } 75 76 return $this; 77 } 78 79 /** 80 * Cast to array 81 * 82 * @return array 83 */ 84 public function toArray() 85 { 86 $array = []; 87 $transform = function ($letters) { 88 $letter = array_shift($letters); 89 return '_' . strtolower($letter); 90 }; 91 foreach ($this as $key => $value) { 92 if ($key === '__strictMode__') { 93 continue; 94 } 95 $normalizedKey = preg_replace_callback('/([A-Z])/', $transform, $key); 96 $array[$normalizedKey] = $value; 97 } 98 return $array; 99 } 100 101 /** 102 * Set a configuration property 103 * 104 * @see ParameterObject::__set() 105 * @param string $key 106 * @param mixed $value 107 * @throws Exception\BadMethodCallException 108 * @return void 109 */ 110 public function __set($key, $value) 111 { 112 $setter = 'set' . str_replace('_', '', $key); 113 114 if (is_callable([$this, $setter])) { 115 $this->{$setter}($value); 116 117 return; 118 } 119 120 if ($this->__strictMode__) { 121 throw new Exception\BadMethodCallException(sprintf( 122 'The option "%s" does not have a callable "%s" ("%s") setter method which must be defined', 123 $key, 124 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))), 125 $setter 126 )); 127 } 128 } 129 130 /** 131 * Get a configuration property 132 * 133 * @see ParameterObject::__get() 134 * @param string $key 135 * @throws Exception\BadMethodCallException 136 * @return mixed 137 */ 138 public function __get($key) 139 { 140 $getter = 'get' . str_replace('_', '', $key); 141 142 if (is_callable([$this, $getter])) { 143 return $this->{$getter}(); 144 } 145 146 throw new Exception\BadMethodCallException(sprintf( 147 'The option "%s" does not have a callable "%s" getter method which must be defined', 148 $key, 149 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))) 150 )); 151 } 152 153 /** 154 * Test if a configuration property is null 155 * @see ParameterObject::__isset() 156 * @param string $key 157 * @return bool 158 */ 159 public function __isset($key) 160 { 161 $getter = 'get' . str_replace('_', '', $key); 162 163 return method_exists($this, $getter) && null !== $this->__get($key); 164 } 165 166 /** 167 * Set a configuration property to NULL 168 * 169 * @see ParameterObject::__unset() 170 * @param string $key 171 * @throws Exception\InvalidArgumentException 172 * @return void 173 */ 174 public function __unset($key) 175 { 176 try { 177 $this->__set($key, null); 178 } catch (Exception\BadMethodCallException $e) { 179 throw new Exception\InvalidArgumentException( 180 'The class property $' . $key . ' cannot be unset as' 181 . ' NULL is an invalid value for it', 182 0, 183 $e 184 ); 185 } 186 } 187} 188