no message
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "nette/component-model",
|
||||
"description": "⚛ Nette Component Model",
|
||||
"keywords": ["nette", "components"],
|
||||
"homepage": "https://nette.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"nette/utils": "^2.5 || ~3.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.0",
|
||||
"tracy/tracy": "^2.3"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/nette": "<2.2",
|
||||
"nette/application": "<2.4"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Framework under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
The BSD License is recommended for most projects. It is easy to understand and it
|
||||
places almost no restrictions on what you can do with the framework. If the GPL
|
||||
fits better to your project, you can use the framework under this license.
|
||||
|
||||
You don't have to notify anyone which license you are using. You can freely
|
||||
use Nette Framework in commercial projects as long as the copyright header
|
||||
remains intact.
|
||||
|
||||
Please be advised that the name "Nette Framework" is a protected trademark and its
|
||||
usage has some limitations. So please do not use word "Nette" in the name of your
|
||||
project or top-level domain, and choose a name that stands on its own merits.
|
||||
If your stuff is good, it will not take long to establish a reputation for yourselves.
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Framework" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
Nette Component Model
|
||||
=====================
|
||||
|
||||
[](https://packagist.org/packages/nette/component-model)
|
||||
[](https://travis-ci.org/nette/component-model)
|
||||
[](https://coveralls.io/github/nette/component-model?branch=master)
|
||||
[](https://github.com/nette/component-model/releases)
|
||||
[](https://github.com/nette/component-model/blob/master/license.md)
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Components are the foundation of reusable code. They make your work easier and allow you to profit from community work. Components are wonderful.
|
||||
Nette Framework introduces several classes and interfaces for all these types of components.
|
||||
|
||||
Documentation can be found on the [website](https://doc.nette.org/components).
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The recommended way to install is via Composer:
|
||||
|
||||
```
|
||||
composer require nette/component-model
|
||||
```
|
||||
|
||||
It requires PHP version 5.6 and supports PHP up to 7.2. The dev-master version requires PHP 7.1.
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\ComponentModel;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of \ArrayAccess for IContainer.
|
||||
*/
|
||||
trait ArrayAccess
|
||||
{
|
||||
/**
|
||||
* Adds the component to the container.
|
||||
* @param string|int $name
|
||||
* @param IComponent $component
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($name, $component)
|
||||
{
|
||||
$this->addComponent($component, $name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns component specified by name. Throws exception if component doesn't exist.
|
||||
* @param string|int $name
|
||||
* @return IComponent
|
||||
* @throws Nette\InvalidArgumentException
|
||||
*/
|
||||
public function offsetGet($name)
|
||||
{
|
||||
return $this->getComponent($name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does component specified by name exists?
|
||||
* @param string|int $name
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($name)
|
||||
{
|
||||
return $this->getComponent($name, false) !== null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes component from the container.
|
||||
* @param string|int $name
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($name)
|
||||
{
|
||||
if ($component = $this->getComponent($name, false)) {
|
||||
$this->removeComponent($component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\ComponentModel;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Component is the base class for all components.
|
||||
*
|
||||
* Components are objects implementing IComponent. They has parent component and own name.
|
||||
*
|
||||
* @property-read string $name
|
||||
* @property-read IContainer|null $parent
|
||||
*/
|
||||
abstract class Component implements IComponent
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var IContainer|null */
|
||||
private $parent;
|
||||
|
||||
/** @var string|null */
|
||||
private $name;
|
||||
|
||||
/** @var array of [type => [obj, depth, path, array of [attached, detached]]] */
|
||||
private $monitors = [];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
list($parent, $name) = func_get_args() + [null, null];
|
||||
if ($parent !== null) {
|
||||
trigger_error(__METHOD__ . '() argument $parent is deprecated, use $parent->addComponent() instead.', E_USER_DEPRECATED);
|
||||
$parent->addComponent($this, $name);
|
||||
|
||||
} elseif ($name !== null) {
|
||||
trigger_error(__METHOD__ . '() argument $name is deprecated, use $parent->setParent(null, $name) instead.', E_USER_DEPRECATED);
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the closest ancestor specified by class or interface name.
|
||||
* @param string|null $type
|
||||
* @param bool $throw
|
||||
* @return IComponent|null
|
||||
*/
|
||||
public function lookup($type, $throw = true)
|
||||
{
|
||||
if (!isset($this->monitors[$type])) { // not monitored or not processed yet
|
||||
$obj = $this->parent;
|
||||
$path = self::NAME_SEPARATOR . $this->name;
|
||||
$depth = 1;
|
||||
while ($obj !== null) {
|
||||
$parent = $obj->getParent();
|
||||
if ($type ? $obj instanceof $type : $parent === null) {
|
||||
break;
|
||||
}
|
||||
$path = self::NAME_SEPARATOR . $obj->getName() . $path;
|
||||
$depth++;
|
||||
$obj = $parent; // IComponent::getParent()
|
||||
if ($obj === $this) {
|
||||
$obj = null; // prevent cycling
|
||||
}
|
||||
}
|
||||
|
||||
if ($obj) {
|
||||
$this->monitors[$type] = [$obj, $depth, substr($path, 1), []];
|
||||
|
||||
} else {
|
||||
$this->monitors[$type] = [null, null, null, []]; // not found
|
||||
}
|
||||
}
|
||||
|
||||
if ($throw && $this->monitors[$type][0] === null) {
|
||||
throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'.");
|
||||
}
|
||||
|
||||
return $this->monitors[$type][0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the closest ancestor specified by class or interface name and returns backtrace path.
|
||||
* A path is the concatenation of component names separated by self::NAME_SEPARATOR.
|
||||
* @param string|null $type
|
||||
* @param bool $throw
|
||||
* @return string|null
|
||||
*/
|
||||
public function lookupPath($type = null, $throw = true)
|
||||
{
|
||||
$this->lookup($type, $throw);
|
||||
return $this->monitors[$type][2];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts monitoring of ancestors.
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
final public function monitor($type, callable $attached = null, callable $detached = null)
|
||||
{
|
||||
if (func_num_args() === 1) {
|
||||
$attached = [$this, 'attached'];
|
||||
$detached = [$this, 'detached'];
|
||||
}
|
||||
if (($obj = $this->lookup($type, false)) && $attached && !in_array([$attached, $detached], $this->monitors[$type][3], true)) {
|
||||
$attached($obj);
|
||||
}
|
||||
$this->monitors[$type][3][] = [$attached, $detached]; // mark as monitored
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stops monitoring of ancestors.
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
public function unmonitor($type)
|
||||
{
|
||||
unset($this->monitors[$type]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method will be called when the component (or component's parent)
|
||||
* becomes attached to a monitored object. Do not call this method yourself.
|
||||
* @param IComponent $obj
|
||||
* @return void
|
||||
* @deprecated use monitor($type, $attached)
|
||||
*/
|
||||
protected function attached($obj)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method will be called before the component (or component's parent)
|
||||
* becomes detached from a monitored object. Do not call this method yourself.
|
||||
* @param IComponent $obj
|
||||
* @return void
|
||||
* @deprecated use monitor($type, null, $detached)
|
||||
*/
|
||||
protected function detached($obj)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/********************* interface IComponent ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parent container if any.
|
||||
* @return IContainer|null
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets or removes the parent of this component. This method is managed by containers and should
|
||||
* not be called by applications
|
||||
* @param string $name
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException
|
||||
* @internal
|
||||
*/
|
||||
public function setParent(IContainer $parent = null, $name = null)
|
||||
{
|
||||
if ($parent === null && $this->parent === null && $name !== null) {
|
||||
$this->name = $name; // just rename
|
||||
return $this;
|
||||
|
||||
} elseif ($parent === $this->parent && $name === null) {
|
||||
return $this; // nothing to do
|
||||
}
|
||||
|
||||
// A component cannot be given a parent if it already has a parent.
|
||||
if ($this->parent !== null && $parent !== null) {
|
||||
throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
|
||||
}
|
||||
|
||||
// remove from parent?
|
||||
if ($parent === null) {
|
||||
$this->refreshMonitors(0);
|
||||
$this->parent = null;
|
||||
|
||||
} else { // add to parent
|
||||
$this->validateParent($parent);
|
||||
$this->parent = $parent;
|
||||
if ($name !== null) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
$tmp = [];
|
||||
$this->refreshMonitors(0, $tmp);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is called by a component when it is about to be set new parent. Descendant can
|
||||
* override this method to disallow a parent change by throwing an Nette\InvalidStateException
|
||||
* @return void
|
||||
* @throws Nette\InvalidStateException
|
||||
*/
|
||||
protected function validateParent(IContainer $parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refreshes monitors.
|
||||
* @param int $depth
|
||||
* @param array|null $missing (array = attaching, null = detaching)
|
||||
* @param array $listeners
|
||||
* @return void
|
||||
*/
|
||||
private function refreshMonitors($depth, &$missing = null, &$listeners = [])
|
||||
{
|
||||
if ($this instanceof IContainer) {
|
||||
foreach ($this->getComponents() as $component) {
|
||||
if ($component instanceof self) {
|
||||
$component->refreshMonitors($depth + 1, $missing, $listeners);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($missing === null) { // detaching
|
||||
foreach ($this->monitors as $type => $rec) {
|
||||
if (isset($rec[1]) && $rec[1] > $depth) {
|
||||
if ($rec[3]) { // monitored
|
||||
$this->monitors[$type] = [null, null, null, $rec[3]];
|
||||
foreach ($rec[3] as $pair) {
|
||||
$listeners[] = [$pair[1], $rec[0]];
|
||||
}
|
||||
} else { // not monitored, just randomly cached
|
||||
unset($this->monitors[$type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else { // attaching
|
||||
foreach ($this->monitors as $type => $rec) {
|
||||
if (isset($rec[0])) { // is in cache yet
|
||||
continue;
|
||||
|
||||
} elseif (!$rec[3]) { // not monitored, just randomly cached
|
||||
unset($this->monitors[$type]);
|
||||
|
||||
} elseif (isset($missing[$type])) { // known from previous lookup
|
||||
$this->monitors[$type] = [null, null, null, $rec[3]];
|
||||
|
||||
} else {
|
||||
$this->monitors[$type] = null; // forces re-lookup
|
||||
if ($obj = $this->lookup($type, false)) {
|
||||
foreach ($rec[3] as $pair) {
|
||||
$listeners[] = [$pair[0], $obj];
|
||||
}
|
||||
} else {
|
||||
$missing[$type] = true;
|
||||
}
|
||||
$this->monitors[$type][3] = $rec[3]; // mark as monitored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($depth === 0) { // call listeners
|
||||
$prev = [];
|
||||
foreach ($listeners as $item) {
|
||||
if ($item[0] && !in_array($item, $prev, true)) {
|
||||
call_user_func($item[0], $item[1]);
|
||||
$prev[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* cloneable, serializable ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Object cloning.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->parent === null) {
|
||||
return;
|
||||
|
||||
} elseif ($this->parent instanceof Container) {
|
||||
$this->parent = $this->parent->_isCloning();
|
||||
if ($this->parent === null) { // not cloning
|
||||
$this->refreshMonitors(0);
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->parent = null;
|
||||
$this->refreshMonitors(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevents serialization.
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevents unserialization.
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\ComponentModel;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* ComponentContainer is default implementation of IContainer.
|
||||
*
|
||||
* @property-read \Iterator $components
|
||||
*/
|
||||
class Container extends Component implements IContainer
|
||||
{
|
||||
/** @var IComponent[] */
|
||||
private $components = [];
|
||||
|
||||
/** @var Container|null */
|
||||
private $cloning;
|
||||
|
||||
|
||||
/********************* interface IContainer ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Adds the component to the container.
|
||||
* @param string|int|null $name
|
||||
* @param string|int $insertBefore
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException
|
||||
*/
|
||||
public function addComponent(IComponent $component, $name, $insertBefore = null)
|
||||
{
|
||||
if ($name === null) {
|
||||
$name = $component->getName();
|
||||
}
|
||||
|
||||
if (is_int($name)) {
|
||||
$name = (string) $name;
|
||||
|
||||
} elseif (!is_string($name)) {
|
||||
throw new Nette\InvalidArgumentException(sprintf('Component name must be integer or string, %s given.', gettype($name)));
|
||||
|
||||
} elseif (!preg_match('#^[a-zA-Z0-9_]+\z#', $name)) {
|
||||
throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given.");
|
||||
}
|
||||
|
||||
if (isset($this->components[$name])) {
|
||||
throw new Nette\InvalidStateException("Component with name '$name' already exists.");
|
||||
}
|
||||
|
||||
// check circular reference
|
||||
$obj = $this;
|
||||
do {
|
||||
if ($obj === $component) {
|
||||
throw new 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 ($k === $insertBefore) {
|
||||
$tmp[$name] = $component;
|
||||
}
|
||||
$tmp[$k] = $v;
|
||||
}
|
||||
$this->components = $tmp;
|
||||
} else {
|
||||
$this->components[$name] = $component;
|
||||
}
|
||||
|
||||
try {
|
||||
$component->setParent($this, $name);
|
||||
} catch (\Exception $e) {
|
||||
unset($this->components[$name]); // undo
|
||||
throw $e;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the component from the container.
|
||||
* @return void
|
||||
*/
|
||||
public function removeComponent(IComponent $component)
|
||||
{
|
||||
$name = $component->getName();
|
||||
if (!isset($this->components[$name]) || $this->components[$name] !== $component) {
|
||||
throw new 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 string|int $name
|
||||
* @param bool $throw throw exception if component doesn't exist?
|
||||
* @return IComponent|null
|
||||
*/
|
||||
public function getComponent($name, $throw = true)
|
||||
{
|
||||
if (!is_int($name) && !is_string($name)) {
|
||||
throw new Nette\InvalidArgumentException(sprintf('Component name must be integer or string, %s given.', gettype($name)));
|
||||
}
|
||||
|
||||
list($name) = $parts = explode(self::NAME_SEPARATOR, (string) $name, 2);
|
||||
|
||||
if (!isset($this->components[$name])) {
|
||||
if (!preg_match('#^[a-zA-Z0-9_]+\z#', $name)) {
|
||||
if ($throw) {
|
||||
throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$component = $this->createComponent($name);
|
||||
if ($component) {
|
||||
if (!$component instanceof IComponent) {
|
||||
throw new Nette\UnexpectedValueException('Method createComponent() did not return Nette\ComponentModel\IComponent.');
|
||||
|
||||
} elseif (!isset($this->components[$name])) {
|
||||
$this->addComponent($component, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$component = isset($this->components[$name]) ? $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 Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$parts[1]' component.");
|
||||
}
|
||||
|
||||
} elseif ($throw) {
|
||||
$hint = Nette\Utils\ObjectHelpers::getSuggestion(array_merge(
|
||||
array_keys($this->components),
|
||||
array_map('lcfirst', preg_filter('#^createComponent([A-Z0-9].*)#', '$1', get_class_methods($this)))
|
||||
), $name);
|
||||
throw new Nette\InvalidArgumentException("Component with name '$name' does not exist" . ($hint ? ", did you mean '$hint'?" : '.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Component factory. Delegates the creation of components to a createComponent<Name> method.
|
||||
* @param string $name
|
||||
* @return IComponent|null
|
||||
*/
|
||||
protected function createComponent($name)
|
||||
{
|
||||
$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 = get_class($this);
|
||||
throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
|
||||
}
|
||||
return $component;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iterates over descendants components.
|
||||
* @param bool $deep return all descendant?
|
||||
* @param string $filterType class type to return
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function getComponents($deep = false, $filterType = null)
|
||||
{
|
||||
$iterator = new RecursiveComponentIterator($this->components);
|
||||
if ($deep) {
|
||||
$deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
|
||||
$iterator = new \RecursiveIteratorIterator($iterator, $deep);
|
||||
}
|
||||
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 Nette\InvalidStateException.
|
||||
* @return void
|
||||
* @throws Nette\InvalidStateException
|
||||
*/
|
||||
protected function validateChildComponent(IComponent $child)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/********************* cloneable, serializable ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Object cloning.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->components) {
|
||||
$oldMyself = reset($this->components)->getParent();
|
||||
$oldMyself->cloning = $this;
|
||||
foreach ($this->components as $name => $component) {
|
||||
$this->components[$name] = clone $component;
|
||||
}
|
||||
$oldMyself->cloning = null;
|
||||
}
|
||||
parent::__clone();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is container cloning now?
|
||||
* @return Container|null
|
||||
* @internal
|
||||
*/
|
||||
public function _isCloning()
|
||||
{
|
||||
return $this->cloning;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\ComponentModel;
|
||||
|
||||
|
||||
/**
|
||||
* Provides functionality required by all components.
|
||||
*/
|
||||
interface IComponent
|
||||
{
|
||||
/** Separator for component names in path concatenation. */
|
||||
const NAME_SEPARATOR = '-';
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
function getName();
|
||||
|
||||
/**
|
||||
* Returns the parent container if any.
|
||||
* @return IContainer|null
|
||||
*/
|
||||
function getParent();
|
||||
|
||||
/**
|
||||
* Sets the parent of this component.
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
function setParent(IContainer $parent = null, $name = null);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\ComponentModel;
|
||||
|
||||
|
||||
/**
|
||||
* Containers are objects that logically contain zero or more IComponent components.
|
||||
*/
|
||||
interface IContainer extends IComponent
|
||||
{
|
||||
/**
|
||||
* Adds the component to the container.
|
||||
* @param string|int|null $name
|
||||
* @return static
|
||||
*/
|
||||
function addComponent(IComponent $component, $name);
|
||||
|
||||
/**
|
||||
* Removes the component from the container.
|
||||
* @return void
|
||||
*/
|
||||
function removeComponent(IComponent $component);
|
||||
|
||||
/**
|
||||
* Returns component specified by name or path.
|
||||
* @param string|int $name
|
||||
* @return IComponent|null
|
||||
*/
|
||||
function getComponent($name);
|
||||
|
||||
/**
|
||||
* Iterates over descendants components.
|
||||
* @return \Iterator
|
||||
*/
|
||||
function getComponents();
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\ComponentModel;
|
||||
|
||||
|
||||
/**
|
||||
* Recursive component iterator. See Container::getComponents().
|
||||
* @internal
|
||||
*/
|
||||
class RecursiveComponentIterator extends \RecursiveArrayIterator implements \Countable
|
||||
{
|
||||
/**
|
||||
* Has the current element has children?
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
return $this->current() instanceof IContainer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The sub-iterator for the current element.
|
||||
* @return \RecursiveIterator
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->current()->getComponents();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the count of elements.
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return iterator_count($this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user