PluginProbe
Packeta / 2.0.9
Packeta v2.0.9
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / component-model / src / ComponentModel / Container.php

Container.php in Packeta 2.0.9, at deps/nette/component-model/src/ComponentModel/Container.php

187 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\ComponentModel;
9
10 use Packetery\Nette;
11 /**
12 * ComponentContainer is default implementation of IContainer.
13 *
14 * @property-read \Iterator $components
15 */
16 class Container extends Component implements IContainer
17 {
18 private const NameRegexp = '#^[a-zA-Z0-9_]+$#D';
19 /** @var IComponent[] */
20 private $components = [];
21 /** @var Container|null */
22 private $cloning;
23 /********************* interface IContainer ****************d*g**/
24 /**
25 * Adds the component to the container.
26 * @return static
27 * @throws \Packetery\Nette\InvalidStateException
28 */
29 public function addComponent(IComponent $component, ?string $name, ?string $insertBefore = null)
30 {
31 if ($name === null) {
32 $name = $component->getName();
33 if ($name === null) {
34 throw new \Packetery\Nette\InvalidStateException("Missing component's name.");
35 }
36 }
37 if (!\preg_match(self::NameRegexp, $name)) {
38 throw new \Packetery\Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '{$name}' given.");
39 }
40 if (isset($this->components[$name])) {
41 throw new \Packetery\Nette\InvalidStateException("Component with name '{$name}' already exists.");
42 }
43 // check circular reference
44 $obj = $this;
45 do {
46 if ($obj === $component) {
47 throw new \Packetery\Nette\InvalidStateException("Circular reference detected while adding component '{$name}'.");
48 }
49 $obj = $obj->getParent();
50 } while ($obj !== null);
51 // user checking
52 $this->validateChildComponent($component);
53 if (isset($this->components[$insertBefore])) {
54 $tmp = [];
55 foreach ($this->components as $k => $v) {
56 if ((string) $k === $insertBefore) {
57 $tmp[$name] = $component;
58 }
59 $tmp[$k] = $v;
60 }
61 $this->components = $tmp;
62 } else {
63 $this->components[$name] = $component;
64 }
65 try {
66 $component->setParent($this, $name);
67 } catch (\Throwable $e) {
68 unset($this->components[$name]);
69 // undo
70 throw $e;
71 }
72 return $this;
73 }
74 /**
75 * Removes the component from the container.
76 */
77 public function removeComponent(IComponent $component) : void
78 {
79 $name = $component->getName();
80 if (($this->components[$name] ?? null) !== $component) {
81 throw new \Packetery\Nette\InvalidArgumentException("Component named '{$name}' is not located in this container.");
82 }
83 unset($this->components[$name]);
84 $component->setParent(null);
85 }
86 /**
87 * Returns component specified by name or path.
88 * @param bool $throw throw exception if component doesn't exist?
89 */
90 public final function getComponent(string $name, bool $throw = \true) : ?IComponent
91 {
92 [$name] = $parts = \explode(self::NameSeparator, $name, 2);
93 if (!isset($this->components[$name])) {
94 if (!\preg_match(self::NameRegexp, $name)) {
95 if ($throw) {
96 throw new \Packetery\Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '{$name}' given.");
97 }
98 return null;
99 }
100 $component = $this->createComponent($name);
101 if ($component && !isset($this->components[$name])) {
102 $this->addComponent($component, $name);
103 }
104 }
105 $component = $this->components[$name] ?? null;
106 if ($component !== null) {
107 if (!isset($parts[1])) {
108 return $component;
109 } elseif ($component instanceof IContainer) {
110 return $component->getComponent($parts[1], $throw);
111 } elseif ($throw) {
112 throw new \Packetery\Nette\InvalidArgumentException("Component with name '{$name}' is not container and cannot have '{$parts[1]}' component.");
113 }
114 } elseif ($throw) {
115 $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);
116 throw new \Packetery\Nette\InvalidArgumentException("Component with name '{$name}' does not exist" . ($hint ? ", did you mean '{$hint}'?" : '.'));
117 }
118 return null;
119 }
120 /**
121 * Component factory. Delegates the creation of components to a createComponent<Name> method.
122 */
123 protected function createComponent(string $name) : ?IComponent
124 {
125 $ucname = \ucfirst($name);
126 $method = 'createComponent' . $ucname;
127 if ($ucname !== $name && \method_exists($this, $method) && (new \ReflectionMethod($this, $method))->getName() === $method) {
128 $component = $this->{$method}($name);
129 if (!$component instanceof IComponent && !isset($this->components[$name])) {
130 $class = static::class;
131 throw new \Packetery\Nette\UnexpectedValueException("Method {$class}::{$method}() did not return or create the desired component.");
132 }
133 return $component;
134 }
135 return null;
136 }
137 /**
138 * Iterates over descendants components.
139 * @return \Iterator<int|string,IComponent>
140 */
141 public final function getComponents(bool $deep = \false, ?string $filterType = null) : \Iterator
142 {
143 $iterator = new RecursiveComponentIterator($this->components);
144 if ($deep) {
145 $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
146 }
147 if ($filterType) {
148 $iterator = new \CallbackFilterIterator($iterator, function ($item) use($filterType) {
149 return $item instanceof $filterType;
150 });
151 }
152 return $iterator;
153 }
154 /**
155 * Descendant can override this method to disallow insert a child by throwing an \Packetery\Nette\InvalidStateException.
156 * @throws \Packetery\Nette\InvalidStateException
157 */
158 protected function validateChildComponent(IComponent $child) : void
159 {
160 }
161 /********************* cloneable, serializable ****************d*g**/
162 /**
163 * Object cloning.
164 */
165 public function __clone()
166 {
167 if ($this->components) {
168 $oldMyself = \reset($this->components)->getParent();
169 \assert($oldMyself instanceof self);
170 $oldMyself->cloning = $this;
171 foreach ($this->components as $name => $component) {
172 $this->components[$name] = clone $component;
173 }
174 $oldMyself->cloning = null;
175 }
176 parent::__clone();
177 }
178 /**
179 * Is container cloning now?
180 * @internal
181 */
182 public final function _isCloning() : ?self
183 {
184 return $this->cloning;
185 }
186 }
187