$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); } } } }