123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- <?php
- namespace Grav\Common;
- use Grav\Common\Language\Language;
- use Grav\Common\Page\Medium\ImageMedium;
- use Grav\Common\Page\Pages;
- use Grav\Common\Service\ConfigServiceProvider;
- use Grav\Common\Service\ErrorServiceProvider;
- use Grav\Common\Service\LoggerServiceProvider;
- use Grav\Common\Service\StreamsServiceProvider;
- use Grav\Common\Twig\Twig;
- use RocketTheme\Toolbox\DI\Container;
- use RocketTheme\Toolbox\Event\Event;
- use RocketTheme\Toolbox\Event\EventDispatcher;
- /**
- * Grav
- *
- * @author Andy Miller
- * @link http://www.rockettheme.com
- * @license http://opensource.org/licenses/MIT
- *
- * Influenced by Pico, Stacey, Kirby, PieCrust and other great platforms...
- */
- class Grav extends Container
- {
- /**
- * @var string
- */
- public $output;
- /**
- * @var static
- */
- protected static $instance;
- public static function instance(array $values = array())
- {
- if (!self::$instance) {
- self::$instance = static::load($values);
- GravTrait::setGrav(self::$instance);
- } elseif ($values) {
- $instance = self::$instance;
- foreach ($values as $key => $value) {
- $instance->offsetSet($key, $value);
- }
- }
- return self::$instance;
- }
- protected static function load(array $values)
- {
- $container = new static($values);
- $container['grav'] = $container;
- $container['debugger'] = new Debugger();
- $container['debugger']->startTimer('_init', 'Initialize');
- $container->register(new LoggerServiceProvider);
- $container->register(new ErrorServiceProvider);
- $container['uri'] = function ($c) {
- return new Uri($c);
- };
- $container['task'] = function ($c) {
- return !empty($_POST['task']) ? $_POST['task'] : $c['uri']->param('task');
- };
- $container['events'] = function ($c) {
- return new EventDispatcher;
- };
- $container['cache'] = function ($c) {
- return new Cache($c);
- };
- $container['session'] = function ($c) {
- return new Session($c);
- };
- $container['plugins'] = function ($c) {
- return new Plugins();
- };
- $container['themes'] = function ($c) {
- return new Themes($c);
- };
- $container['twig'] = function ($c) {
- return new Twig($c);
- };
- $container['taxonomy'] = function ($c) {
- return new Taxonomy($c);
- };
- $container['language'] = function ($c) {
- return new Language($c);
- };
- $container['pages'] = function ($c) {
- return new Page\Pages($c);
- };
- $container['assets'] = new Assets();
- $container['page'] = function ($c) {
- /** @var Pages $pages */
- $pages = $c['pages'];
- /** @var Language $language */
- $language = $c['language'];
- /** @var Uri $uri */
- $uri = $c['uri'];
- $path = rtrim($uri->path(), '/');
- $path = $path ?: '/';
- $page = $pages->dispatch($path);
- // Redirection tests
- if ($page) {
- // Language-specific redirection scenarios
- if ($language->enabled()) {
- if ($language->isLanguageInUrl() && !$language->isIncludeDefaultLanguage()) {
- $c->redirect($page->route());
- }
- if (!$language->isLanguageInUrl() && $language->isIncludeDefaultLanguage()) {
- $c->redirectLangSafe($page->route());
- }
- }
- // Default route test and redirect
- if ($c['config']->get('system.pages.redirect_default_route') && $page->route() != $path) {
- $c->redirectLangSafe($page->route());
- }
- }
- // if page is not found, try some fallback stuff
- if (!$page || !$page->routable()) {
- // Try fallback URL stuff...
- $c->fallbackUrl($page, $path);
- // If no page found, fire event
- $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());
- };
- $container['browser'] = function ($c) {
- return new Browser();
- };
- $container['base_url_absolute'] = function ($c) {
- return $c['config']->get('system.base_url_absolute') ?: $c['uri']->rootUrl(true);
- };
- $container['base_url_relative'] = function ($c) {
- return $c['config']->get('system.base_url_relative') ?: $c['uri']->rootUrl(false);
- };
- $container['base_url'] = function ($c) {
- return $c['config']->get('system.absolute_urls') ? $c['base_url_absolute'] : $c['base_url_relative'];
- };
- $container->register(new StreamsServiceProvider);
- $container->register(new ConfigServiceProvider);
- $container['inflector'] = new Inflector();
- $container['debugger']->stopTimer('_init');
- return $container;
- }
- public function process()
- {
- /** @var Debugger $debugger */
- $debugger = $this['debugger'];
- // Initialize configuration.
- $debugger->startTimer('_config', 'Configuration');
- $this['config']->init();
- $this['errors']->resetHandlers();
- $this['uri']->init();
- $this['session']->init();
- $debugger->init();
- $this['config']->debug();
- $debugger->stopTimer('_config');
- // Use output buffering to prevent headers from being sent too early.
- ob_start();
- if ($this['config']->get('system.cache.gzip')) {
- ob_start('ob_gzhandler');
- }
- // Initialize the timezone
- if ($this['config']->get('system.timezone')) {
- date_default_timezone_set($this['config']->get('system.timezone'));
- }
- // Initialize Locale if set and configured
- if ($this['language']->enabled() && $this['config']->get('system.languages.override_locale')) {
- setlocale(LC_ALL, $this['language']->getLanguage());
- } elseif ($this['config']->get('system.default_locale')) {
- setlocale(LC_ALL, $this['config']->get('system.default_locale'));
- }
- $debugger->startTimer('streams', 'Streams');
- $this['streams'];
- $debugger->stopTimer('streams');
- $debugger->startTimer('plugins', 'Plugins');
- $this['plugins']->init();
- $this->fireEvent('onPluginsInitialized');
- $debugger->stopTimer('plugins');
- $debugger->startTimer('themes', 'Themes');
- $this['themes']->init();
- $this->fireEvent('onThemeInitialized');
- $debugger->stopTimer('themes');
- $task = $this['task'];
- if ($task) {
- $this->fireEvent('onTask.' . $task);
- }
- $this['assets']->init();
- $this->fireEvent('onAssetsInitialized');
- $debugger->startTimer('twig', 'Twig');
- $this['twig']->init();
- $debugger->stopTimer('twig');
- $debugger->startTimer('pages', 'Pages');
- $this['pages']->init();
- $this->fireEvent('onPagesInitialized');
- $debugger->stopTimer('pages');
- $this->fireEvent('onPageInitialized');
- $debugger->addAssets();
- // Process whole page as required
- $debugger->startTimer('render', 'Render');
- $this->output = $this['output'];
- $this->fireEvent('onOutputGenerated');
- $debugger->stopTimer('render');
- // Set the header type
- $this->header();
- echo $this->output;
- $debugger->render();
- $this->fireEvent('onOutputRendered');
- register_shutdown_function([$this, 'shutdown']);
- }
- /**
- * Redirect browser to another location.
- *
- * @param string $route Internal route.
- * @param int $code Redirection code (30x)
- */
- public function redirect($route, $code = null)
- {
- /** @var Uri $uri */
- $uri = $this['uri'];
- //Check for code in route
- $regex = '/.*(\[(30[1-7])\])$/';
- preg_match($regex, $route, $matches);
- if ($matches) {
- $route = str_replace($matches[1], '', $matches[0]);
- $code = $matches[2];
- }
- if ($code == null) {
- $code = $this['config']->get('system.pages.redirect_default_code', 301);
- }
- if (isset($this['session'])) {
- $this['session']->close();
- }
- if ($uri->isExternal($route)) {
- $url = $route;
- } else {
- $url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/');
- }
- header("Location: {$url}", true, $code);
- exit();
- }
- /**
- * Redirect browser to another location taking language into account (preferred)
- *
- * @param string $route Internal route.
- * @param int $code Redirection code (30x)
- */
- public function redirectLangSafe($route, $code = null)
- {
- /** @var Language $language */
- $language = $this['language'];
- if (!$this['uri']->isExternal($route) && $language->enabled() && $language->isIncludeDefaultLanguage()) {
- return $this->redirect($language->getLanguage() . $route, $code);
- } else {
- return $this->redirect($route, $code);
- }
- }
- /**
- * Returns mime type for the file format.
- *
- * @param string $format
- * @return string
- */
- public function mime($format)
- {
- switch ($format) {
- case 'json':
- return 'application/json';
- case 'html':
- return 'text/html';
- case 'atom':
- return 'application/atom+xml';
- case 'rss':
- return 'application/rss+xml';
- case 'xml':
- return 'application/xml';
- }
- return 'text/html';
- }
- /**
- * Set response header.
- */
- public function header()
- {
- $extension = $this['uri']->extension();
- /** @var Page $page */
- $page = $this['page'];
- header('Content-type: ' . $this->mime($extension));
- // Calculate Expires Headers if set to > 0
- $expires = $page->expires();
- if ($expires > 0) {
- $expires_date = gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT';
- header('Cache-Control: max-age=' . $expires);
- header('Expires: '. $expires_date);
- }
- // Set the last modified time
- if ($page->lastModified()) {
- $last_modified_date = gmdate('D, d M Y H:i:s', $page->modified()) . ' GMT';
- header('Last-Modified: ' . $last_modified_date);
- }
- // Calculate a Hash based on the raw file
- if ($page->eTag()) {
- header('ETag: ' . md5($page->raw() . $page->modified()));
- }
- // Set debugger data in headers
- if (!($extension === null || $extension == 'html')) {
- $this['debugger']->enabled(false);
- }
- // Set HTTP response code
- if (isset($this['page']->header()->http_response_code)) {
- http_response_code($this['page']->header()->http_response_code);
- }
- // Vary: Accept-Encoding
- if ($this['config']->get('system.pages.vary_accept_encoding', false)) {
- header('Vary: Accept-Encoding');
- }
- }
- /**
- * Fires an event with optional parameters.
- *
- * @param string $eventName
- * @param Event $event
- * @return Event
- */
- public function fireEvent($eventName, Event $event = null)
- {
- /** @var EventDispatcher $events */
- $events = $this['events'];
- return $events->dispatch($eventName, $event);
- }
- /**
- * Set the final content length for the page and flush the buffer
- *
- */
- public function shutdown()
- {
- if ($this['config']->get('system.debugger.shutdown.close_connection')) {
- //stop user abort
- if (function_exists('ignore_user_abort')) {
- @ignore_user_abort(true);
- }
- // close the session
- if (isset($this['session'])) {
- $this['session']->close();
- }
- // flush buffer if gzip buffer was started
- if ($this['config']->get('system.cache.gzip')) {
- ob_end_flush(); // gzhandler buffer
- }
- // get lengh and close the connection
- header('Content-Length: ' . ob_get_length());
- header("Connection: close");
- // flush the regular buffer
- ob_end_flush();
- @ob_flush();
- flush();
- // fix for fastcgi close connection issue
- if (function_exists('fastcgi_finish_request')) {
- @fastcgi_finish_request();
- }
- }
- $this->fireEvent('onShutdown');
- }
- /**
- * This attempts to find media, other files, and download them
- * @param $page
- * @param $path
- */
- protected function fallbackUrl($page, $path)
- {
- /** @var Uri $uri */
- $uri = $this['uri'];
- /** @var Config $config */
- $config = $this['config'];
- $uri_extension = $uri->extension();
- // Only allow whitelisted types to fallback
- if (!in_array($uri_extension, $config->get('system.pages.fallback_types'))) {
- return;
- }
- $path_parts = pathinfo($path);
- $page = $this['pages']->dispatch($path_parts['dirname'], true);
- if ($page) {
- $media = $page->media()->all();
- $parsed_url = parse_url(urldecode($uri->basename()));
- $media_file = $parsed_url['path'];
- // if this is a media object, try actions first
- if (isset($media[$media_file])) {
- $medium = $media[$media_file];
- foreach ($uri->query(null, true) as $action => $params) {
- if (in_array($action, ImageMedium::$magic_actions)) {
- call_user_func_array(array(&$medium, $action), explode(',', $params));
- }
- }
- Utils::download($medium->path(), false);
- }
- // unsupported media type, try to download it...
- if ($uri_extension) {
- $extension = $uri_extension;
- } else {
- if (isset($path_parts['extension'])) {
- $extension = $path_parts['extension'];
- } else {
- $extension = null;
- }
- }
- if ($extension) {
- $download = true;
- if (in_array(ltrim($extension, '.'), $config->get('system.media.unsupported_inline_types', []))) {
- $download = false;
- }
- Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), $download);
- }
- }
- }
- }
|