no message

This commit is contained in:
BarboraFleg
2019-04-04 18:34:40 +02:00
commit ce48ca4a21
589 changed files with 82184 additions and 0 deletions
+38
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,29 @@
Nette Component Model
=====================
[![Downloads this Month](https://img.shields.io/packagist/dm/nette/component-model.svg)](https://packagist.org/packages/nette/component-model)
[![Build Status](https://travis-ci.org/nette/component-model.svg?branch=master)](https://travis-ci.org/nette/component-model)
[![Coverage Status](https://coveralls.io/repos/github/nette/component-model/badge.svg?branch=master)](https://coveralls.io/github/nette/component-model?branch=master)
[![Latest Stable Version](https://poser.pugx.org/nette/component-model/v/stable)](https://github.com/nette/component-model/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](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();
}
@@ -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);
}
}