4
* This file is part of the Nette Framework (http://nette.org)
6
* Copyright (c) 2004 David Grudl (http://davidgrudl.com)
8
* For the full copyright and license information, please view
9
* the file license.txt that was distributed with this source code.
12
namespace Nette\ComponentModel;
18
* ComponentContainer is default implementation of IContainer.
22
* @property-read \ArrayIterator $components
24
class Container extends Component implements IContainer
26
/** @var IComponent[] */
27
private $components = array();
29
/** @var IComponent|NULL */
33
/********************* interface IContainer ****************d*g**/
37
* Adds the specified component to the IContainer.
42
* @throws Nette\InvalidStateException
44
public function addComponent(IComponent $component, $name, $insertBefore = NULL)
47
$name = $component->getName();
51
$name = (string) $name;
53
} elseif (!is_string($name)) {
54
throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
56
} elseif (!preg_match('#^[a-zA-Z0-9_]+\z#', $name)) {
57
throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given.");
60
if (isset($this->components[$name])) {
61
throw new Nette\InvalidStateException("Component with name '$name' already exists.");
64
// check circular reference
67
if ($obj === $component) {
68
throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'.");
70
$obj = $obj->getParent();
71
} while ($obj !== NULL);
74
$this->validateChildComponent($component);
77
if (isset($this->components[$insertBefore])) {
79
foreach ($this->components as $k => $v) {
80
if ($k === $insertBefore) {
81
$tmp[$name] = $component;
85
$this->components = $tmp;
87
$this->components[$name] = $component;
89
$component->setParent($this, $name);
91
} catch (\Exception $e) {
92
unset($this->components[$name]); // undo
100
* Removes a component from the IContainer.
103
public function removeComponent(IComponent $component)
105
$name = $component->getName();
106
if (!isset($this->components[$name]) || $this->components[$name] !== $component) {
107
throw new Nette\InvalidArgumentException("Component named '$name' is not located in this container.");
110
unset($this->components[$name]);
111
$component->setParent(NULL);
116
* Returns component specified by name or path.
118
* @param bool throw exception if component doesn't exist?
119
* @return IComponent|NULL
121
final public function getComponent($name, $need = TRUE)
124
$name = (string) $name;
126
} elseif (!is_string($name)) {
127
throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
130
$a = strpos($name, self::NAME_SEPARATOR);
132
$ext = (string) substr($name, $a + 1);
133
$name = substr($name, 0, $a);
137
throw new Nette\InvalidArgumentException("Component or subcomponent name must not be empty string.");
141
if (!isset($this->components[$name])) {
142
$component = $this->createComponent($name);
143
if ($component instanceof IComponent && $component->getParent() === NULL) {
144
$this->addComponent($component, $name);
148
if (isset($this->components[$name])) {
150
return $this->components[$name];
152
} elseif ($this->components[$name] instanceof IContainer) {
153
return $this->components[$name]->getComponent($ext, $need);
156
throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component.");
160
throw new Nette\InvalidArgumentException("Component with name '$name' does not exist.");
166
* Component factory. Delegates the creation of components to a createComponent<Name> method.
167
* @param string component name
168
* @return IComponent the created component (optionally)
170
protected function createComponent($name)
172
$ucname = ucfirst($name);
173
$method = 'createComponent' . $ucname;
174
if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) {
175
$component = $this->$method($name);
176
if (!$component instanceof IComponent && !isset($this->components[$name])) {
177
$class = get_class($this);
178
throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
186
* Iterates over a components.
187
* @param bool recursive?
188
* @param string class types filter
189
* @return \ArrayIterator
191
final public function getComponents($deep = FALSE, $filterType = NULL)
193
$iterator = new RecursiveComponentIterator($this->components);
195
$deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
196
$iterator = new \RecursiveIteratorIterator($iterator, $deep);
199
$iterator = new Nette\Iterators\InstanceFilter($iterator, $filterType);
206
* Descendant can override this method to disallow insert a child by throwing an Nette\InvalidStateException.
208
* @throws Nette\InvalidStateException
210
protected function validateChildComponent(IComponent $child)
215
/********************* cloneable, serializable ****************d*g**/
221
public function __clone()
223
if ($this->components) {
224
$oldMyself = reset($this->components)->getParent();
225
$oldMyself->cloning = $this;
226
foreach ($this->components as $name => $component) {
227
$this->components[$name] = clone $component;
229
$oldMyself->cloning = NULL;
236
* Is container cloning now?
237
* @return NULL|IComponent
240
public function _isCloning()
242
return $this->cloning;