~ubuntu-branches/debian/experimental/php-nette/experimental

« back to all changes in this revision

Viewing changes to Nette-2.0.13/Nette/ComponentModel/Container.php

  • Committer: Package Import Robot
  • Author(s): David Prévot
  • Date: 2013-11-30 08:47:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20131130084754-4udf1xsu9085tnfc
Tags: 2.1.0~rc-1
* New upstream branch
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
 
3
 
/**
4
 
 * This file is part of the Nette Framework (http://nette.org)
5
 
 *
6
 
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7
 
 *
8
 
 * For the full copyright and license information, please view
9
 
 * the file license.txt that was distributed with this source code.
10
 
 */
11
 
 
12
 
namespace Nette\ComponentModel;
13
 
 
14
 
use Nette;
15
 
 
16
 
 
17
 
/**
18
 
 * ComponentContainer is default implementation of IContainer.
19
 
 *
20
 
 * @author     David Grudl
21
 
 *
22
 
 * @property-read \ArrayIterator $components
23
 
 */
24
 
class Container extends Component implements IContainer
25
 
{
26
 
        /** @var IComponent[] */
27
 
        private $components = array();
28
 
 
29
 
        /** @var IComponent|NULL */
30
 
        private $cloning;
31
 
 
32
 
 
33
 
        /********************* interface IContainer ****************d*g**/
34
 
 
35
 
 
36
 
        /**
37
 
         * Adds the specified component to the IContainer.
38
 
         * @param  IComponent
39
 
         * @param  string
40
 
         * @param  string
41
 
         * @return self
42
 
         * @throws Nette\InvalidStateException
43
 
         */
44
 
        public function addComponent(IComponent $component, $name, $insertBefore = NULL)
45
 
        {
46
 
                if ($name === NULL) {
47
 
                        $name = $component->getName();
48
 
                }
49
 
 
50
 
                if (is_int($name)) {
51
 
                        $name = (string) $name;
52
 
 
53
 
                } elseif (!is_string($name)) {
54
 
                        throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
55
 
 
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.");
58
 
                }
59
 
 
60
 
                if (isset($this->components[$name])) {
61
 
                        throw new Nette\InvalidStateException("Component with name '$name' already exists.");
62
 
                }
63
 
 
64
 
                // check circular reference
65
 
                $obj = $this;
66
 
                do {
67
 
                        if ($obj === $component) {
68
 
                                throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'.");
69
 
                        }
70
 
                        $obj = $obj->getParent();
71
 
                } while ($obj !== NULL);
72
 
 
73
 
                // user checking
74
 
                $this->validateChildComponent($component);
75
 
 
76
 
                try {
77
 
                        if (isset($this->components[$insertBefore])) {
78
 
                                $tmp = array();
79
 
                                foreach ($this->components as $k => $v) {
80
 
                                        if ($k === $insertBefore) {
81
 
                                                $tmp[$name] = $component;
82
 
                                        }
83
 
                                        $tmp[$k] = $v;
84
 
                                }
85
 
                                $this->components = $tmp;
86
 
                        } else {
87
 
                                $this->components[$name] = $component;
88
 
                        }
89
 
                        $component->setParent($this, $name);
90
 
 
91
 
                } catch (\Exception $e) {
92
 
                        unset($this->components[$name]); // undo
93
 
                        throw $e;
94
 
                }
95
 
                return $this;
96
 
        }
97
 
 
98
 
 
99
 
        /**
100
 
         * Removes a component from the IContainer.
101
 
         * @return void
102
 
         */
103
 
        public function removeComponent(IComponent $component)
104
 
        {
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.");
108
 
                }
109
 
 
110
 
                unset($this->components[$name]);
111
 
                $component->setParent(NULL);
112
 
        }
113
 
 
114
 
 
115
 
        /**
116
 
         * Returns component specified by name or path.
117
 
         * @param  string
118
 
         * @param  bool   throw exception if component doesn't exist?
119
 
         * @return IComponent|NULL
120
 
         */
121
 
        final public function getComponent($name, $need = TRUE)
122
 
        {
123
 
                if (is_int($name)) {
124
 
                        $name = (string) $name;
125
 
 
126
 
                } elseif (!is_string($name)) {
127
 
                        throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
128
 
 
129
 
                } else {
130
 
                        $a = strpos($name, self::NAME_SEPARATOR);
131
 
                        if ($a !== FALSE) {
132
 
                                $ext = (string) substr($name, $a + 1);
133
 
                                $name = substr($name, 0, $a);
134
 
                        }
135
 
 
136
 
                        if ($name === '') {
137
 
                                throw new Nette\InvalidArgumentException("Component or subcomponent name must not be empty string.");
138
 
                        }
139
 
                }
140
 
 
141
 
                if (!isset($this->components[$name])) {
142
 
                        $component = $this->createComponent($name);
143
 
                        if ($component instanceof IComponent && $component->getParent() === NULL) {
144
 
                                $this->addComponent($component, $name);
145
 
                        }
146
 
                }
147
 
 
148
 
                if (isset($this->components[$name])) {
149
 
                        if (!isset($ext)) {
150
 
                                return $this->components[$name];
151
 
 
152
 
                        } elseif ($this->components[$name] instanceof IContainer) {
153
 
                                return $this->components[$name]->getComponent($ext, $need);
154
 
 
155
 
                        } elseif ($need) {
156
 
                                throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component.");
157
 
                        }
158
 
 
159
 
                } elseif ($need) {
160
 
                        throw new Nette\InvalidArgumentException("Component with name '$name' does not exist.");
161
 
                }
162
 
        }
163
 
 
164
 
 
165
 
        /**
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)
169
 
         */
170
 
        protected function createComponent($name)
171
 
        {
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.");
179
 
                        }
180
 
                        return $component;
181
 
                }
182
 
        }
183
 
 
184
 
 
185
 
        /**
186
 
         * Iterates over a components.
187
 
         * @param  bool    recursive?
188
 
         * @param  string  class types filter
189
 
         * @return \ArrayIterator
190
 
         */
191
 
        final public function getComponents($deep = FALSE, $filterType = NULL)
192
 
        {
193
 
                $iterator = new RecursiveComponentIterator($this->components);
194
 
                if ($deep) {
195
 
                        $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
196
 
                        $iterator = new \RecursiveIteratorIterator($iterator, $deep);
197
 
                }
198
 
                if ($filterType) {
199
 
                        $iterator = new Nette\Iterators\InstanceFilter($iterator, $filterType);
200
 
                }
201
 
                return $iterator;
202
 
        }
203
 
 
204
 
 
205
 
        /**
206
 
         * Descendant can override this method to disallow insert a child by throwing an Nette\InvalidStateException.
207
 
         * @return void
208
 
         * @throws Nette\InvalidStateException
209
 
         */
210
 
        protected function validateChildComponent(IComponent $child)
211
 
        {
212
 
        }
213
 
 
214
 
 
215
 
        /********************* cloneable, serializable ****************d*g**/
216
 
 
217
 
 
218
 
        /**
219
 
         * Object cloning.
220
 
         */
221
 
        public function __clone()
222
 
        {
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;
228
 
                        }
229
 
                        $oldMyself->cloning = NULL;
230
 
                }
231
 
                parent::__clone();
232
 
        }
233
 
 
234
 
 
235
 
        /**
236
 
         * Is container cloning now?
237
 
         * @return NULL|IComponent
238
 
         * @internal
239
 
         */
240
 
        public function _isCloning()
241
 
        {
242
 
                return $this->cloning;
243
 
        }
244
 
 
245
 
}