diff --git a/composer.json b/composer.json index 3e9f78461..5b2913006 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "erusev/parsedown-extra": "dev-master", "symfony/yaml": "2.5.*@dev", "symfony/console": "2.5.*@dev", + "symfony/event-dispatcher": "2.5.*@dev", "doctrine/cache": "1.4.*@dev", "tracy/tracy": "dev-master", "gregwar/image": "dev-master", diff --git a/index.php b/index.php index cf7025a4c..b9ead227d 100644 --- a/index.php +++ b/index.php @@ -30,7 +30,7 @@ try { $grav->process(); } catch (\Exception $e) { - $grav->fireEvent('onFatalException', $e); + $grav->fireEvent('onFatalException'); throw $e; } diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php index 28a6359ba..b6266ed75 100644 --- a/system/src/Grav/Common/Assets.php +++ b/system/src/Grav/Common/Assets.php @@ -1,7 +1,6 @@ get('system.base_url_relative')); - $theme = $config->get('system.pages.theme'); + $theme = trim($config->get('system.pages.theme')); $asset_config = (array)$config->get('system.assets'); $this->config($asset_config); diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index 1de34b00e..d4dd899d6 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -3,6 +3,8 @@ namespace Grav\Common; use Grav\Common\Service\StreamsServiceProvider; use Grav\Component\DI\Container; +use Grav\Component\EventDispatcher\Event; +use Grav\Component\EventDispatcher\EventDispatcher; use Grav\Component\Filesystem\ResourceLocator; /** @@ -53,6 +55,9 @@ class Grav extends Container $container['grav'] = $container; + $container['events'] = function ($c) { + return new EventDispatcher; + }; $container['uri'] = function ($c) { return new Uri($c); }; @@ -81,7 +86,18 @@ class Grav extends Container return new Assets(); }; $container['page'] = function ($c) { - return $c['pages']->dispatch($c['uri']->route()); + $page = $c['pages']->dispatch($c['uri']->route()); + + if (!$page || !$page->routable()) { + $event = $c->fireEvent('onPageNotFound'); + + if (isset($event->page)) { + $page = $event->page; + } else { + throw new \RuntimeException('Page Not Found', 404); + } + } + return $page; }; $container['output'] = function ($c) { return $c['twig']->processSite($c['uri']->extension()); @@ -115,11 +131,6 @@ class Grav extends Container $this->fireEvent('onAfterGetPage'); - // If there's no page, throw exception - if (!$this['page']) { - throw new \RuntimeException('Page Not Found', 404); - } - // Process whole page as required $this->output = $this['output']; @@ -179,34 +190,16 @@ class Grav extends Container } /** - * Processes any hooks and runs them. + * Fires an event with optional parameters. + * + * @param string $eventName + * @param Event $event + * @return Event */ - public function fireEvent() + public function fireEvent($eventName, Event $event = null) { - $args = func_get_args(); - $hook_id = array_shift($args); - $no_timing_hooks = array('onAfterPageProcessed','onAfterFolderProcessed', 'onAfterCollectionProcessed'); - - /** @var Plugins $plugins */ - $plugins = $this['plugins']; - - if (!empty($plugins)) { - foreach ($plugins as $plugin) { - if (is_callable(array($plugin, $hook_id))) { - call_user_func_array(array($plugin, $hook_id), $args); - } - } - } - - /** @var Config $config */ - $config = $this['config']; - - if ($config && $config->get('system.debugger.log.timing') && !in_array($hook_id, $no_timing_hooks)) { - /** @var Debugger $debugger */ - $debugger = isset($this['debugger']) ? $this['debugger'] : null; - if ($debugger) { - $debugger->log($hook_id.': %f ms'); - } - } + /** @var EventDispatcher $events */ + $events = $this['events']; + return $events->dispatch($eventName, $event); } } diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 9685bdd46..dfced9a75 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -1,18 +1,19 @@ fireEvent('onAfterCollectionProcessed', $collection); + $grav->fireEvent('onAfterCollectionProcessed', new Event(['collection' => $collection])); $params = $collection->params(); diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index a3e29e23f..f853b97ff 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -1,13 +1,14 @@ initialized) { - $this->grav->fireEvent('onCreateBlueprint', $blueprint); + $this->grav->fireEvent('onCreateBlueprint', new Event(['blueprint' => $blueprint])); $blueprint->initialized = true; } @@ -420,7 +421,7 @@ class Pages $page->init($file); if ($config->get('system.pages.events.page')) { - $this->grav->fireEvent('onAfterPageProcessed', $page); + $this->grav->fireEvent('onAfterPageProcessed', new Event(['page' => $page])); } } elseif ($file->isDir() && !$file->isDot()) { @@ -447,7 +448,7 @@ class Pages $this->lastModified($file->getMTime()); if ($config->get('system.pages.events.page')) { - $this->grav->fireEvent('onAfterFolderProcessed', $page); + $this->grav->fireEvent('onAfterFolderProcessed', new Event(['page' => $page])); } } } diff --git a/system/src/Grav/Common/Plugin.php b/system/src/Grav/Common/Plugin.php index fb25dccdb..0371cd41a 100644 --- a/system/src/Grav/Common/Plugin.php +++ b/system/src/Grav/Common/Plugin.php @@ -1,26 +1,75 @@ grav = $grav; $this->config = $config; } + + /** + * @param array $events + */ + protected function enable(array $events) + { + /** @var EventDispatcher $dispatcher */ + $dispatcher = $this->grav['events']; + + foreach ($events as $eventName => $params) { + if (is_string($params)) { + $dispatcher->addListener($eventName, array($this, $params)); + } elseif (is_string($params[0])) { + $dispatcher->addListener($eventName, array($this, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $dispatcher->addListener($eventName, array($this, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } } diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php index 50278e10b..e1b336f58 100644 --- a/system/src/Grav/Common/Plugins.php +++ b/system/src/Grav/Common/Plugins.php @@ -2,6 +2,8 @@ namespace Grav\Common; use Grav\Common\Filesystem\File; +use Grav\Component\EventDispatcher\EventDispatcher; +use Grav\Component\EventDispatcher\EventSubscriberInterface; /** * The Plugins object holds an array of all the plugin objects that @@ -30,7 +32,9 @@ class Plugins extends Iterator $config = $this->grav['config']; $plugins = (array) $config->get('plugins'); - $instances = ['theme' => $this->grav['themes']->load()]; + /** @var EventDispatcher $events */ + $events = $this->grav['events']; + foreach ($plugins as $plugin => $data) { if (empty($data['enabled'])) { // Only load enabled plugins. @@ -50,10 +54,18 @@ class Plugins extends Iterator throw new \RuntimeException(sprintf("Plugin '%s' class not found!", $plugin)); } - $instances[$pluginClass] = new $pluginClass($config); + $instance = new $pluginClass($this->grav, $config); + if ($instance instanceof EventSubscriberInterface) { + $events->addSubscriber($instance); + } } - return $instances; + $instance = $this->grav['themes']->load(); + if ($instance instanceof EventSubscriberInterface) { + $events->addSubscriber($instance); + } + + return $this->items; } public function add($plugin) diff --git a/system/src/Grav/Common/Theme.php b/system/src/Grav/Common/Theme.php index 31e5cf477..0b3e17017 100644 --- a/system/src/Grav/Common/Theme.php +++ b/system/src/Grav/Common/Theme.php @@ -1,6 +1,6 @@ grav['config']; + if (!$name) { - /** @var Config $config */ - $config = $this->grav['config']; $name = $config->get('system.pages.theme'); } @@ -100,13 +101,13 @@ class Themes $className = '\\Grav\\Theme\\' . ucfirst($name); if (class_exists($className)) { - $class = new $className; + $class = new $className($this->grav, $config); } } } if (empty($class)) { - $class = new Theme; + $class = new Theme($this->grav, $config); } return $class; diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index 1db8c1ac5..af7cad26a 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -26,21 +26,29 @@ class Uri */ public function __construct() { - $base = 'http://'; - $uri = $_SERVER["REQUEST_URI"]; - if (isset($_SERVER["HTTPS"])) { - $base = (@$_SERVER["HTTPS"] == "on") ? "https://" : "http://"; + $base = 'http://'; + $uri = $_SERVER['REQUEST_URI']; + $root_path = rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/'); + + + if (isset($_SERVER['HTTPS'])) { + $base = (@$_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; } - $base .= $_SERVER["SERVER_NAME"]; + $base .= $_SERVER['SERVER_NAME']; - if ($_SERVER["SERVER_PORT"] != "80" && $_SERVER["SERVER_PORT"] != "443") { - $base .= ":".$_SERVER["SERVER_PORT"]; + if ($_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443') { + $base .= ":".$_SERVER['SERVER_PORT']; + } + + // check if userdir in the path and workaround PHP bug with PHP_SELF + if (strpos($_SERVER['REQUEST_URI'], '/~') !== false && strpos($_SERVER['PHP_SELF'], '/~') === false) { + $root_path = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '/', 1)) . $root_path; } $this->base = $base; - $this->root = $base . rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/'); + $this->root = $base . $root_path; $this->url = $base . $uri; $this->init(); diff --git a/system/src/Grav/Component/ArrayTraits/ArrayAccess.php b/system/src/Grav/Component/ArrayTraits/ArrayAccess.php new file mode 100644 index 000000000..aacd1c01f --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/ArrayAccess.php @@ -0,0 +1,46 @@ +items[$offset]); + } + + /** + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return isset($this->items[$offset]) ? $this->items[$offset] : null; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->items[$offset] = $value; + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) + { + unset($this->items[$offset]); + } +} diff --git a/system/src/Grav/Component/ArrayTraits/Constructor.php b/system/src/Grav/Component/ArrayTraits/Constructor.php new file mode 100644 index 000000000..80779c2fc --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/Constructor.php @@ -0,0 +1,21 @@ +items = $items; + } +} diff --git a/system/src/Grav/Component/ArrayTraits/Countable.php b/system/src/Grav/Component/ArrayTraits/Countable.php new file mode 100644 index 000000000..10e51410c --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/Countable.php @@ -0,0 +1,19 @@ +items); + } +} diff --git a/system/src/Grav/Component/ArrayTraits/Getters.php b/system/src/Grav/Component/ArrayTraits/Getters.php new file mode 100644 index 000000000..90da77a24 --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/Getters.php @@ -0,0 +1,50 @@ +offsetSet($offset, $value); + } + + /** + * Magic getter method + * + * @param mixed $offset Asset name value + * @return mixed Asset value + */ + public function __get($offset) + { + return $this->offsetGet($offset); + } + + /** + * Magic method to determine if the attribute is set + * + * @param mixed $offset Asset name value + * @return boolean True if the value is set + */ + public function __isset($offset) + { + return $this->offsetExists($offset); + } + + /** + * Magic method to unset the attribute + * + * @param mixed $offset The name value to unset + */ + public function __unset($offset) + { + $this->offsetUnset($offset); + } +} diff --git a/system/src/Grav/Component/EventDispatcher/Event.php b/system/src/Grav/Component/EventDispatcher/Event.php new file mode 100644 index 000000000..ba2ad9075 --- /dev/null +++ b/system/src/Grav/Component/EventDispatcher/Event.php @@ -0,0 +1,15 @@ +