shared = $config['shared']; } if (isset($config['multiple'])) { $this->multiple = $config['multiple']; } unset($config['shared'], $config['multiple']); $this->instances = $config; } /** * Adding config * * @param array config */ public function config(array $config) { if (isset($config['shared'])) { $this->shared = array_replace_recursive($this->shared, $config['shared']); } if (isset($config['multiple'])) { $this->multiple = array_replace_recursive($this->multiple, $config['multiple']); } unset($config['shared'], $config['multiple']); $this->config = array_replace_recursive($this->config, $config); } /** * Gets a service or parameter. * * @param string $id * * @throws InvalidArgumentException * * @return mixed */ public function __get($id) { if (isset($this->instances[$id])) { return $this->instances[$id]; } elseif (strpos($id, '.') !== false) { $tree = explode('.', $id); $service = $this->__get(array_shift($tree)); if (is_array($service)) { return $this->fromArray($service, $tree); } elseif (is_object($service)) { return $service->{$tree[0]}; } else { return null; } } if (isset($this->shared[$id])) { $toShare = true; $config = (array) $this->shared[$id]; } elseif (isset($this->multiple[$id])) { $toShare = false; $config = (array) $this->multiple[$id]; } else { throw new InvalidArgumentException('Wrong property name: ' . $id); } // N.B. "class" is just the first element, regardless of its key $class = array_shift($config); $args = []; // If you want to susbtitute some values in arguments, use non-numeric keys for them foreach ($config as $k => $v) { $args[] = is_numeric($k) ? $v : $this->resolve($v); } // Special case: reference to factory method if ($class{0} == '@' && strpos($class, ':') !== false) { list($name, $method) = explode(':', substr($class, 1), 2); $factory = $this->__get($name); $service = $factory->$method(...$args); } else { // Adding this container in the arguments for constructor $args[] = $this; $service = new $class(...$args); } if ($toShare) { $this->instances[$id] = $service; } return $service; } /** * Sets a service or parameter. * Provides a fluent interface. * * @param string $id * @param mixed $service */ public function __set($id, $service) { if (strpos($id, '.') !== false) { //???? } else { $this->instances[$id] = $service; } } /** * Gets data from array. * * @param array $array * @param array $tree * * @return mixed */ public function fromArray(array $array, array $tree) { $ptr = & $array; foreach ($tree as $s) { if (isset($ptr[$s])) { $ptr = & $ptr[$s]; } else { return null; } } return $ptr; } /** * Sets a parameter. * Provides a fluent interface. * * @param string $name The parameter name * @param mixed $value The parameter value * * @throws InvalidArgumentException * * @return ContainerInterface Self reference */ public function setParameter($name, $value) { $segments = explode('.', $name); $n = count($segments); $ptr = &$this->config; foreach ($segments as $s) { if (--$n) { if (! array_key_exists($s, $ptr)) { $ptr[$s] = []; } elseif (! is_array($ptr[$s])) { throw new InvalidArgumentException("Scalar '{$s}' in the path '{$name}'"); } $ptr = &$ptr[$s]; } else { $ptr[$s] = $value; } } return $this; } protected function resolve($value) { if (is_string($value)) { if (strpos($value, '%') !== false) { // whole string substitution can return any type of value if (preg_match('~^%([a-z0-9_]+(?:\.[a-z0-9_]+)*)%$~i', $value, $matches)) { $value = $this->__get($matches[1]); } else { // partial string substitution casts value to string $value = preg_replace_callback( '~%([a-z0-9_]+(?:\.[a-z0-9_]+)*)%~i', function ($matches) { return $this->__get($matches[1]); }, $value ); } } elseif (isset($value{0}) && $value{0} === '@') { return $this->__get(substr($value, 1)); } } elseif (is_array($value)) { foreach ($value as &$v) { $v = $this->resolve($v); } unset($v); } return $value; } }