From 244428a2fe00f086cfe7e3f3e8392376d72d9b7a Mon Sep 17 00:00:00 2001 From: kremsy Date: Tue, 18 Feb 2014 18:15:12 +0100 Subject: [PATCH] curl-easy / event dispatcher lib --- .../Component/EventDispatcher/.gitignore | 3 + .../Component/EventDispatcher/CHANGELOG.md | 16 + .../ContainerAwareEventDispatcher.php | 202 ++++++++++ .../TraceableEventDispatcherInterface.php | 32 ++ .../Component/EventDispatcher/Event.php | 129 +++++++ .../EventDispatcher/EventDispatcher.php | 185 ++++++++++ .../EventDispatcherInterface.php | 96 +++++ .../EventSubscriberInterface.php | 50 +++ .../EventDispatcher/GenericEvent.php | 186 ++++++++++ .../ImmutableEventDispatcher.php | 92 +++++ .../Symfony/Component/EventDispatcher/LICENSE | 19 + .../Component/EventDispatcher/README.md | 25 ++ .../ContainerAwareEventDispatcherTest.php | 244 ++++++++++++ .../Tests/EventDispatcherTest.php | 346 ++++++++++++++++++ .../EventDispatcher/Tests/EventTest.php | 84 +++++ .../Tests/GenericEventTest.php | 140 +++++++ .../Tests/ImmutableEventDispatcherTest.php | 105 ++++++ .../Component/EventDispatcher/composer.json | 38 ++ .../EventDispatcher/phpunit.xml.dist | 30 ++ .../core/Libs/curl-easy/cURL/Collection.php | 81 ++++ .../core/Libs/curl-easy/cURL/Error.php | 6 + .../core/Libs/curl-easy/cURL/Event.php | 8 + .../core/Libs/curl-easy/cURL/Exception.php | 6 + .../core/Libs/curl-easy/cURL/Options.php | 68 ++++ .../core/Libs/curl-easy/cURL/Request.php | 150 ++++++++ .../Libs/curl-easy/cURL/RequestInterface.php | 12 + .../Libs/curl-easy/cURL/RequestsQueue.php | 235 ++++++++++++ .../curl-easy/cURL/RequestsQueueInterface.php | 13 + .../core/Libs/curl-easy/cURL/Response.php | 83 +++++ 29 files changed, 2684 insertions(+) create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/.gitignore create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/CHANGELOG.md create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Event.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcher.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/EventSubscriberInterface.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/GenericEvent.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/LICENSE create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/README.md create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventTest.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/composer.json create mode 100644 application/core/Libs/Symfony/Component/EventDispatcher/phpunit.xml.dist create mode 100644 application/core/Libs/curl-easy/cURL/Collection.php create mode 100644 application/core/Libs/curl-easy/cURL/Error.php create mode 100644 application/core/Libs/curl-easy/cURL/Event.php create mode 100644 application/core/Libs/curl-easy/cURL/Exception.php create mode 100644 application/core/Libs/curl-easy/cURL/Options.php create mode 100644 application/core/Libs/curl-easy/cURL/Request.php create mode 100644 application/core/Libs/curl-easy/cURL/RequestInterface.php create mode 100644 application/core/Libs/curl-easy/cURL/RequestsQueue.php create mode 100644 application/core/Libs/curl-easy/cURL/RequestsQueueInterface.php create mode 100644 application/core/Libs/curl-easy/cURL/Response.php diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/.gitignore b/application/core/Libs/Symfony/Component/EventDispatcher/.gitignore new file mode 100644 index 00000000..c49a5d8d --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/CHANGELOG.md b/application/core/Libs/Symfony/Component/EventDispatcher/CHANGELOG.md new file mode 100644 index 00000000..536c5ac7 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -0,0 +1,16 @@ +CHANGELOG +========= + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/application/core/Libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 00000000..9448ed43 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param integer $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $key => $l) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + if ($key === $serviceId.'.'.$method) { + if ($listener === array($l, $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (Boolean) count($this->listenerIds) || (Boolean) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach (array_keys($this->listenerIds) as $serviceEventName) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritDoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @throws \InvalidArgumentException if the service is not defined + */ + public function dispatch($eventName, Event $event = null) + { + $this->lazyLoad($eventName); + + return parent::dispatch($eventName, $event); + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/application/core/Libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 00000000..a67a9790 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Event.php b/application/core/Libs/Symfony/Component/EventDispatcher/Event.php new file mode 100644 index 00000000..e25e7f1e --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Event.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +class Event +{ + /** + * @var Boolean Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation + * @return Boolean Whether propagation was already stopped for this event. + * + * @api + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + * + * @api + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event + * + * @param EventDispatcherInterface $dispatcher + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event + * + * @return EventDispatcherInterface + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function getDispatcher() + { + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcher.php b/application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcher.php new file mode 100644 index 00000000..ad48d431 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * + * @api + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch + * + * @api + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if (!isset($this->listeners[$eventName])) { + return $event; + } + + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return $this->sorted; + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + return (Boolean) count($this->getListeners($eventName)); + } + + /** + * @see EventDispatcherInterface::addListener + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * @see EventDispatcherInterface::removeListener + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * @see EventDispatcherInterface::addSubscriber + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * @see EventDispatcherInterface::removeSubscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + call_user_func($listener, $event, $eventName, $this); + if ($event->isPropagationStopped()) { + break; + } + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php new file mode 100644 index 00000000..7aead23b --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + * + * @api + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + * + * @api + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param integer $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string|array $eventName The event(s) to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return Boolean true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/application/core/Libs/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 00000000..080f892f --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + * + * @api + */ + public static function getSubscribedEvents(); +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/GenericEvent.php b/application/core/Libs/Symfony/Component/EventDispatcher/GenericEvent.php new file mode 100644 index 00000000..c226fdf2 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/GenericEvent.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Event subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @throws \InvalidArgumentException If key is not found. + * + * @return mixed Contents of array key. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return boolean + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + * + * @return mixed + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return boolean + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/application/core/Libs/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php new file mode 100644 index 00000000..b70b81a8 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/LICENSE b/application/core/Libs/Symfony/Component/EventDispatcher/LICENSE new file mode 100644 index 00000000..0b3292cf --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/README.md b/application/core/Libs/Symfony/Component/EventDispatcher/README.md new file mode 100644 index 00000000..22bf74fd --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/README.md @@ -0,0 +1,25 @@ +EventDispatcher Component +========================= + +The Symfony2 EventDispatcher component implements the Mediator pattern in a +simple and effective way to make your projects truly extensible. + + use Symfony\Component\EventDispatcher\EventDispatcher; + use Symfony\Component\EventDispatcher\Event; + + $dispatcher = new EventDispatcher(); + + $dispatcher->addListener('event_name', function (Event $event) { + // ... + }); + + $dispatcher->dispatch('event_name'); + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/EventDispatcher/ + $ composer.phar install + $ phpunit diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 00000000..fb3b4caa --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEvent' => array('onEvent', 10), + 'onEvent' => array('onEvent'), + ); + } + + public function onEvent(Event $e) + { + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 00000000..1e3282f8 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,346 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class EventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstance() + { + $dispatcher = null; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10) + )); + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventTest.php b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventTest.php new file mode 100644 index 00000000..7a20fe6b --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/EventTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event(); + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->dispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + public function testSetDispatcher() + { + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + public function testGetDispatcher() + { + $this->assertNull($this->event->getDispatcher()); + } + + public function testGetName() + { + $this->assertNull($this->event->getName()); + } + + public function testSetName() + { + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php new file mode 100644 index 00000000..c1e22f44 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \stdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event'), 'foo'); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs() + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 00000000..80a7e43b --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/composer.json b/application/core/Libs/Symfony/Component/EventDispatcher/composer.json new file mode 100644 index 00000000..e748c506 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } + }, + "target-dir": "Symfony/Component/EventDispatcher", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/application/core/Libs/Symfony/Component/EventDispatcher/phpunit.xml.dist b/application/core/Libs/Symfony/Component/EventDispatcher/phpunit.xml.dist new file mode 100644 index 00000000..0c3de4f7 --- /dev/null +++ b/application/core/Libs/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/application/core/Libs/curl-easy/cURL/Collection.php b/application/core/Libs/curl-easy/cURL/Collection.php new file mode 100644 index 00000000..18a5f036 --- /dev/null +++ b/application/core/Libs/curl-easy/cURL/Collection.php @@ -0,0 +1,81 @@ +data; + } + + /** + * Sets value + * + * @param mixed $key Key + * @param mixed $value Value + * + * @return self + */ + public function set($key, $value = null) + { + if (is_array($key)) { + foreach ($key as $k => $v) { + $this->data[$k] = $v; + } + } else { + $this->data[$key] = $value; + } + return $this; + } + + /** + * Checks if key does exist + * + * @param mixed $key Key + * + * @return bool TRUE if exists, FALSE otherwise + */ + public function has($key) + { + return isset($this->data[$key]); + } + + /** + * Returns value of $key, throws Exception if does not exist + * + * @param mixed $key Key + * + * @return mixed Value of key + */ + public function get($key) + { + if ($this->has($key)) { + return $this->data[$key]; + } else { + throw new Exception('Key does not exist.'); + } + } + + /** + * Removes key + * + * @param mixed $key Key to remove + * + * @return self + */ + public function remove($key) + { + unset($this->data[$key]); + return $this; + } +} diff --git a/application/core/Libs/curl-easy/cURL/Error.php b/application/core/Libs/curl-easy/cURL/Error.php new file mode 100644 index 00000000..6a9725a2 --- /dev/null +++ b/application/core/Libs/curl-easy/cURL/Error.php @@ -0,0 +1,6 @@ +data)) { + curl_setopt_array($request->getHandle(), $this->data); + } + + return $this; + } + + /** + * Prepares array for intelligent setters + * + * @return void + */ + public static function loadCurlConstantsTable() + { + $constants = get_defined_constants(true); + $table = array(); + foreach ($constants['curl'] as $key => $value) { + if (strpos($key, 'CURLOPT_') === 0) { + $key = str_ireplace(array('CURLOPT', '_'), '', $key); + $table[$key] = $value; + } + } + self::$curlConstantsTable = $table; + } + + /** + * Intelligent setters + * + * @param string $name Function name + * @param array $args Arguments + * + * @return self + */ + public function __call($name, $args) + { + if (substr($name, 0, 3) == 'set' && isset($args[0])) { + if (empty(self::$curlConstantsTable)) { + self::loadCurlConstantsTable(); + } + $const = strtoupper(substr($name, 3)); + if (isset(self::$curlConstantsTable[$const])) { + $this->data[self::$curlConstantsTable[$const]] = $args[0]; + return $this; + } else { + throw new Exception('Constant CURLOPT_'.$const.' does not exist.'); + } + } + } +} diff --git a/application/core/Libs/curl-easy/cURL/Request.php b/application/core/Libs/curl-easy/cURL/Request.php new file mode 100644 index 00000000..db155391 --- /dev/null +++ b/application/core/Libs/curl-easy/cURL/Request.php @@ -0,0 +1,150 @@ +getOptions()->set(CURLOPT_URL, $url); + } + $this->ch = curl_init(); + } + + /** + * Closes cURL resource and frees the memory. + * It is neccessary when you make a lot of requests + * and you want to avoid fill up the memory. + * + * @return void + */ + public function __destruct() + { + if (isset($this->ch)) { + curl_close($this->ch); + } + } + + /** + * Get the cURL\Options instance + * Creates empty one if does not exist + * + * @return Options + */ + public function getOptions() + { + if (!isset($this->options)) { + $this->options = new Options; + } + return $this->options; + } + + /** + * Sets Options + * + * @param Options $options Options + * + * @return void + */ + public function setOptions(Options $options) + { + $this->options = $options; + } + + /** + * Returns cURL raw resource + * + * @return resource cURL handle + */ + public function getHandle() + { + return $this->ch; + } + + /** + * Get unique id of cURL handle + * Useful for debugging or logging. + * + * @return int + */ + public function getUID() + { + return (int)$this->ch; + } + + /** + * Perform a cURL session. + * Equivalent to curl_exec(). + * This function should be called after initializing a cURL + * session and all the options for the session are set. + * + * @return Response + */ + public function send() + { + if ($this->options instanceof Options) { + $this->options->applyTo($this); + } + $content = curl_exec($this->ch); + + $response = new Response($this, $content); + $errorCode = curl_errno($this->ch); + if ($errorCode !== CURLE_OK) { + $response->setError(new Error(curl_error($this->ch), $errorCode)); + } + return $response; + } + + protected function prepareQueue() + { + if (!isset($this->queue)) { + $request = $this; + $this->queue = new RequestsQueue; + $this->queue->addListener( + 'complete', + function ($event) use ($request) { + $request->dispatch('complete', $event); + } + ); + $this->queue->attach($this); + } + } + + public function socketPerform() + { + $this->prepareQueue(); + return $this->queue->socketPerform(); + } + + public function socketSelect($timeout = 1) + { + if (!isset($this->queue)) { + throw new Exception('Cannot select without perform before.'); + } + return $this->queue->socketSelect($timeout); + } +} diff --git a/application/core/Libs/curl-easy/cURL/RequestInterface.php b/application/core/Libs/curl-easy/cURL/RequestInterface.php new file mode 100644 index 00000000..88b1d4e1 --- /dev/null +++ b/application/core/Libs/curl-easy/cURL/RequestInterface.php @@ -0,0 +1,12 @@ +mh = curl_multi_init(); + } + + /** + * Destructor, closes curl_multi handler + * + * @return void + */ + public function __destruct() + { + if (isset($this->mh)) { + curl_multi_close($this->mh); + } + } + + /** + * Returns cURL\Options object with default request's options + * + * @return Options + */ + public function getDefaultOptions() + { + if (!isset($this->defaultOptions)) { + $this->defaultOptions = new Options; + } + return $this->defaultOptions; + } + + /** + * Overrides default options with given Options object + * + * @param Options $defaultOptions New options + * + * @return void + */ + public function setDefaultOptions(Options $defaultOptions) + { + $this->defaultOptions = $defaultOptions; + } + + /** + * Get cURL multi handle + * + * @return resource + */ + public function getHandle() + { + return $this->mh; + } + + /** + * Attach request to queue. + * + * @param Request $request Request to add + * + * @return self + */ + public function attach(Request $request) + { + $this->queue[$request->getUID()] = $request; + return $this; + } + + /** + * Detach request from queue. + * + * @param Request $request Request to remove + * + * @return self + */ + public function detach(Request $request) + { + unset($this->queue[$request->getUID()]); + return $this; + } + + /** + * Processes handles which are ready and removes them from pool. + * + * @return int Amount of requests completed + */ + protected function read() + { + $n = 0; + while ($info = curl_multi_info_read($this->mh)) { + $n++; + $request = $this->queue[(int)$info['handle']]; + $result = $info['result']; + + curl_multi_remove_handle($this->mh, $request->getHandle()); + unset($this->running[$request->getUID()]); + $this->detach($request); + + $event = new Event; + $event->request = $request; + $event->response = new Response($request, curl_multi_getcontent($request->getHandle())); + if ($result !== CURLE_OK) { + $event->response->setError(new Error(curl_error($request->getHandle()), $result)); + } + $event->queue = $this; + $this->dispatch('complete', $event); + } + + return $n; + } + + /** + * Returns count of handles in queue + * + * @return int Handles count + */ + public function count() + { + return count($this->queue); + } + + /** + * Executes requests in parallel + * + * @return void + */ + public function send() + { + while ($this->socketPerform()) { + $this->socketSelect(); + } + } + + /** + * Returns requests present in $queue but not in $running + * + * @return array Array of requests + */ + protected function getRequestsNotRunning() + { + return array_diff_key($this->queue, $this->running); + } + + /** + * Download available data on socket. + * + * @return bool TRUE when there are any requests on queue, FALSE when finished + */ + public function socketPerform() + { + if ($this->count() == 0) { + throw new Exception('Cannot perform if there are no requests in queue.'); + } + + + $notRunning = $this->getRequestsNotRunning(); + do { + /** + * Apply cURL options to new requests + */ + foreach ($notRunning as $request) { + $this->getDefaultOptions()->applyTo($request); + $request->getOptions()->applyTo($request); + curl_multi_add_handle($this->mh, $request->getHandle()); + $this->running[$request->getUID()] = $request; + } + + $runningBefore = $this->runningCount; + do { + $mrc = curl_multi_exec($this->mh, $this->runningCount); + } while ($mrc === CURLM_CALL_MULTI_PERFORM); + $runningAfter = $this->runningCount; + + $completed = ($runningAfter < $runningBefore) ? $this->read() : 0; + + $notRunning = $this->getRequestsNotRunning(); + } while (count($notRunning) > 0); + + + return $this->count() > 0; + } + + /** + * Waits until activity on socket + * On success, returns TRUE. On failure, this function will + * return FALSE on a select failure or timeout (from the underlying + * select system call) + * + * @param float $timeout Maximum time to wait + * + * @return bool + */ + public function socketSelect($timeout = 1) + { + if ($this->count() == 0) { + throw new Exception('Cannot select if there are no requests in queue.'); + } + return curl_multi_select($this->mh, $timeout) !== -1; + } +} diff --git a/application/core/Libs/curl-easy/cURL/RequestsQueueInterface.php b/application/core/Libs/curl-easy/cURL/RequestsQueueInterface.php new file mode 100644 index 00000000..85635417 --- /dev/null +++ b/application/core/Libs/curl-easy/cURL/RequestsQueueInterface.php @@ -0,0 +1,13 @@ +ch = $request->getHandle(); + + if (is_string($content)) { + $this->content = $content; + } + } + + /** + * Get information regarding a current transfer + * If opt is given, returns its value as a string + * Otherwise, returns an associative array with + * the following elements (which correspond to opt), or FALSE on failure. + * + * @param int $key One of the CURLINFO_* constants + * + * @return mixed + */ + public function getInfo($key = null) + { + return $key === null ? curl_getinfo($this->ch) : curl_getinfo($this->ch, $key); + } + + /** + * Returns content of request + * + * @return string Content + */ + public function getContent() + { + return $this->content; + } + + /** + * Sets error instance + * + * @param Error $error Error to set + * + * @return void + */ + public function setError(Error $error) + { + $this->error = $error; + } + + /** + * Returns a error instance + * + * @return Error|null + */ + public function getError() + { + return isset($this->error) ? $this->error : null; + } + + /** + * Returns the error number for the last cURL operation. + * + * @return int Returns the error number or 0 (zero) if no error occurred. + */ + public function hasError() + { + return isset($this->error); + } +}