getName(); if ($name === null) { throw new \Packetery\Nette\InvalidStateException("Missing component's name."); } } if (!\preg_match(self::NameRegexp, $name)) { throw new \Packetery\Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '{$name}' given."); } if (isset($this->components[$name])) { throw new \Packetery\Nette\InvalidStateException("Component with name '{$name}' already exists."); } // check circular reference $obj = $this; do { if ($obj === $component) { throw new \Packetery\Nette\InvalidStateException("Circular reference detected while adding component '{$name}'."); } $obj = $obj->getParent(); } while ($obj !== null); // user checking $this->validateChildComponent($component); if (isset($this->components[$insertBefore])) { $tmp = []; foreach ($this->components as $k => $v) { if ((string) $k === $insertBefore) { $tmp[$name] = $component; } $tmp[$k] = $v; } $this->components = $tmp; } else { $this->components[$name] = $component; } try { $component->setParent($this, $name); } catch (\Throwable $e) { unset($this->components[$name]); // undo throw $e; } return $this; } /** * Removes the component from the container. */ public function removeComponent(IComponent $component) : void { $name = $component->getName(); if (($this->components[$name] ?? null) !== $component) { throw new \Packetery\Nette\InvalidArgumentException("Component named '{$name}' is not located in this container."); } unset($this->components[$name]); $component->setParent(null); } /** * Returns component specified by name or path. * @param bool $throw throw exception if component doesn't exist? */ public final function getComponent(string $name, bool $throw = \true) : ?IComponent { [$name] = $parts = \explode(self::NameSeparator, $name, 2); if (!isset($this->components[$name])) { if (!\preg_match(self::NameRegexp, $name)) { if ($throw) { throw new \Packetery\Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '{$name}' given."); } return null; } $component = $this->createComponent($name); if ($component && !isset($this->components[$name])) { $this->addComponent($component, $name); } } $component = $this->components[$name] ?? null; if ($component !== null) { if (!isset($parts[1])) { return $component; } elseif ($component instanceof IContainer) { return $component->getComponent($parts[1], $throw); } elseif ($throw) { throw new \Packetery\Nette\InvalidArgumentException("Component with name '{$name}' is not container and cannot have '{$parts[1]}' component."); } } elseif ($throw) { $hint = \Packetery\Nette\Utils\ObjectHelpers::getSuggestion(\array_merge(\array_map('strval', \array_keys($this->components)), \array_map('lcfirst', \preg_filter('#^createComponent([A-Z0-9].*)#', '$1', \get_class_methods($this)))), $name); throw new \Packetery\Nette\InvalidArgumentException("Component with name '{$name}' does not exist" . ($hint ? ", did you mean '{$hint}'?" : '.')); } return null; } /** * Component factory. Delegates the creation of components to a createComponent method. */ protected function createComponent(string $name) : ?IComponent { $ucname = \ucfirst($name); $method = 'createComponent' . $ucname; if ($ucname !== $name && \method_exists($this, $method) && (new \ReflectionMethod($this, $method))->getName() === $method) { $component = $this->{$method}($name); if (!$component instanceof IComponent && !isset($this->components[$name])) { $class = static::class; throw new \Packetery\Nette\UnexpectedValueException("Method {$class}::{$method}() did not return or create the desired component."); } return $component; } return null; } /** * Iterates over descendants components. * @return \Iterator */ public final function getComponents(bool $deep = \false, ?string $filterType = null) : \Iterator { $iterator = new RecursiveComponentIterator($this->components); if ($deep) { $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); } if ($filterType) { $iterator = new \CallbackFilterIterator($iterator, function ($item) use($filterType) { return $item instanceof $filterType; }); } return $iterator; } /** * Descendant can override this method to disallow insert a child by throwing an \Packetery\Nette\InvalidStateException. * @throws \Packetery\Nette\InvalidStateException */ protected function validateChildComponent(IComponent $child) : void { } /********************* cloneable, serializable ****************d*g**/ /** * Object cloning. */ public function __clone() { if ($this->components) { $oldMyself = \reset($this->components)->getParent(); \assert($oldMyself instanceof self); $oldMyself->cloning = $this; foreach ($this->components as $name => $component) { $this->components[$name] = clone $component; } $oldMyself->cloning = null; } parent::__clone(); } /** * Is container cloning now? * @internal */ public final function _isCloning() : ?self { return $this->cloning; } }