%PDF- %PDF-
Direktori : /www/varak.net/losik.varak.net/vendor/nette/component-model/src/ComponentModel/ |
Current File : //www/varak.net/losik.varak.net/vendor/nette/component-model/src/ComponentModel/Component.php |
<?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); 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<string, array{?IComponent, ?int, ?string, array<int, array{?callable, ?callable}>}> means [type => [obj, depth, path, [attached, detached]]] */ private $monitors = []; /** * Finds the closest ancestor specified by class or interface name. * @param bool $throw throw exception if component doesn't exist? */ final public function lookup(?string $type, bool $throw = true): ?IComponent { if (!isset($this->monitors[$type])) { // not monitored or not processed yet $obj = $this->parent; $path = self::NameSeparator . $this->name; $depth = 1; while ($obj !== null) { $parent = $obj->getParent(); if ($type ? $obj instanceof $type : $parent === null) { break; } $path = self::NameSeparator . $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) { $message = $this->name !== null ? "Component '$this->name' is not attached to '$type'." : "Component of type '" . static::class . "' is not attached to '$type'."; throw new Nette\InvalidStateException($message); } 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. */ final public function lookupPath(?string $type = null, bool $throw = true): ?string { $this->lookup($type, $throw); return $this->monitors[$type][2]; } /** * Starts monitoring of ancestors. */ final public function monitor(string $type, ?callable $attached = null, ?callable $detached = null): void { 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. */ final public function unmonitor(string $type): void { 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. * @deprecated use monitor($type, $attached) */ protected function attached(IComponent $obj): void { } /** * This method will be called before the component (or component's parent) * becomes detached from a monitored object. Do not call this method yourself. * @deprecated use monitor($type, null, $detached) */ protected function detached(IComponent $obj): void { } /********************* interface IComponent ****************d*g**/ final public function getName(): ?string { return $this->name; } /** * Returns the parent container if any. */ final public function getParent(): ?IContainer { return $this->parent; } /** * Sets or removes the parent of this component. This method is managed by containers and should * not be called by applications * @return static * @throws Nette\InvalidStateException * @internal */ public function setParent(?IContainer $parent, ?string $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 * @throws Nette\InvalidStateException */ protected function validateParent(IContainer $parent): void { } /** * Refreshes monitors. * @param array<string,true>|null $missing (array = attaching, null = detaching) * @param array<int,array{callable,IComponent}> $listeners */ private function refreshMonitors(int $depth, ?array &$missing = null, array &$listeners = []): void { 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 { unset($this->monitors[$type]); // 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)) { $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. */ final public function __sleep() { throw new Nette\NotImplementedException('Object serialization is not supported by class ' . static::class); } /** * Prevents unserialization. */ final public function __wakeup() { throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . static::class); } }