- enabled "HTTP/2 Server Push" for static resources, closed #685
This commit is contained in:
@@ -10,6 +10,7 @@ namespace Controller;
|
||||
|
||||
use Controller\Ccp as Ccp;
|
||||
use lib\Config;
|
||||
use lib\Resource;
|
||||
|
||||
class AppController extends Controller {
|
||||
|
||||
@@ -58,7 +59,13 @@ class AppController extends Controller {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function init(\Base $f3) {
|
||||
|
||||
$resource = Resource::instance();
|
||||
$resource->register('script', 'app/login');
|
||||
$resource->register('script', 'app/mappage', 'prefetch');
|
||||
$resource->register('image', 'pf-bg.jpg');
|
||||
$resource->register('image', 'pf-header-bg.jpg');
|
||||
$resource->register('image', 'landing/eve_sso_login_buttons_large_black.png');
|
||||
$resource->register('image', 'landing/eve_sso_login_buttons_large_black_hover.png');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,8 +7,10 @@
|
||||
*/
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Controller\Api as Api;
|
||||
use lib\Config;
|
||||
use lib\Resource;
|
||||
use lib\Monolog;
|
||||
use lib\Socket;
|
||||
use lib\Util;
|
||||
@@ -76,8 +78,7 @@ class Controller {
|
||||
if($f3->get('AJAX')){
|
||||
header('Content-type: application/json');
|
||||
}else{
|
||||
// js path (build/minified or raw uncompressed files)
|
||||
$f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') );
|
||||
$this->initResource($f3);
|
||||
|
||||
$this->setTemplate( Config::getPathfinderData('view.index') );
|
||||
}
|
||||
@@ -91,6 +92,12 @@ class Controller {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function afterroute(\Base $f3){
|
||||
// send preload/prefetch headers
|
||||
$resource = Resource::instance();
|
||||
if($resource->getOption('output') === 'header'){
|
||||
header($resource->buildHeader(), false);
|
||||
}
|
||||
|
||||
if($this->getTemplate()){
|
||||
// Ajax calls don´t need a page render..
|
||||
// this happens on client side
|
||||
@@ -145,6 +152,34 @@ class Controller {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* init new Resource handler
|
||||
* @param \Base $f3
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
protected function initResource(\Base $f3){
|
||||
$resource = Resource::instance();
|
||||
$resource->setOption('filePath', [
|
||||
'style' => $f3->get('BASE') . '/public/css/' . Config::getPathfinderData('version'),
|
||||
'script' => $f3->get('BASE') . '/public/js/' . Config::getPathfinderData('version'),
|
||||
'font' => $f3->get('BASE') . '/public/fonts',
|
||||
'image' => $f3->get('BASE') . '/public/img'
|
||||
]);
|
||||
|
||||
$resource->register('style', 'pathfinder');
|
||||
|
||||
$resource->register('script', 'lib/require');
|
||||
$resource->register('script', 'app');
|
||||
|
||||
$resource->register('font', 'oxygen-regular-webfont');
|
||||
$resource->register('font', 'oxygen-bold-webfont');
|
||||
$resource->register('font', 'fa-regular-400');
|
||||
$resource->register('font', 'fa-solid-900');
|
||||
$resource->register('font', 'fa-brands-400');
|
||||
|
||||
$f3->set('tplResource', $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* get cookies "state" information
|
||||
* -> whether user accepts cookies
|
||||
|
||||
@@ -140,6 +140,8 @@ class Setup extends Controller {
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params): bool {
|
||||
$this->initResource($f3);
|
||||
|
||||
// init dbLib class. Manages all DB connections
|
||||
$this->dbLib = DB\Database::instance();
|
||||
|
||||
@@ -152,9 +154,6 @@ class Setup extends Controller {
|
||||
// body element class
|
||||
$f3->set('tplBodyClass', 'pf-landing');
|
||||
|
||||
// js path (build/minified or raw uncompressed files)
|
||||
$f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
namespace lib;
|
||||
|
||||
|
||||
use controller\LogController;
|
||||
use Exception;
|
||||
|
||||
|
||||
240
app/main/lib/resource.php
Normal file
240
app/main/lib/resource.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 08.09.2018
|
||||
* Time: 10:58
|
||||
*/
|
||||
|
||||
namespace lib;
|
||||
|
||||
|
||||
class Resource extends \Prefab {
|
||||
|
||||
/**
|
||||
* default link "rel" attribute
|
||||
* @link https://w3c.github.io/preload/#x2.link-type-preload
|
||||
*/
|
||||
const ATTR_REL = 'preload';
|
||||
|
||||
/**
|
||||
* default link "as" attributes
|
||||
*/
|
||||
const ATTR_AS = [
|
||||
'style' => 'style',
|
||||
'script' => 'script',
|
||||
'font' => 'font',
|
||||
'image' => 'image'
|
||||
];
|
||||
|
||||
/**
|
||||
* default link "type" attributes
|
||||
*/
|
||||
const ATTR_TYPE = [
|
||||
'font' => 'font/woff2'
|
||||
];
|
||||
|
||||
/**
|
||||
* default additional attributes by $group
|
||||
*/
|
||||
const ATTR_ADD = [
|
||||
'font' => ['crossorigin' => 'anonymous']
|
||||
];
|
||||
|
||||
/**
|
||||
* absolute file path -> use setOption() for update
|
||||
* @var array
|
||||
*/
|
||||
private $filePath = [
|
||||
'style' => '',
|
||||
'script' => '',
|
||||
'font' => '',
|
||||
'image' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* default file extensions by $group
|
||||
* -> used if no fileExtension found in $file
|
||||
* @var array
|
||||
*/
|
||||
private $fileExt = [
|
||||
'style' => 'css',
|
||||
'script' => 'js',
|
||||
'font' => 'woff2'
|
||||
];
|
||||
|
||||
/**
|
||||
* output type
|
||||
* -> 'inline' -> render inline HTML <link> tags
|
||||
* -> 'header' -> send "Link" HTTP Header with request
|
||||
* @see buildLinks()
|
||||
* @see buildHeader()
|
||||
* @var string
|
||||
*/
|
||||
private $output = 'inline';
|
||||
|
||||
/**
|
||||
* resource file cache
|
||||
* @var array
|
||||
*/
|
||||
private $resources = [];
|
||||
|
||||
/**
|
||||
* set option
|
||||
* @param string $option
|
||||
* @param $value
|
||||
*/
|
||||
public function setOption(string $option, $value){
|
||||
$this->$option = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get option
|
||||
* @param string $option
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getOption(string $option){
|
||||
return isset($this->$option) ? $this->$option : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* register new resource $file
|
||||
* @param string $group
|
||||
* @param string $file
|
||||
* @param string $rel
|
||||
*/
|
||||
public function register(string $group, string $file, string $rel = self::ATTR_REL){
|
||||
$this->resources[$group][$file] = ['options' => ['rel' => $rel]];
|
||||
}
|
||||
|
||||
/**
|
||||
* get resource path/file.ext
|
||||
* @param string $group
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
public function getLink(string $group, string $file) : string {
|
||||
$link = $this->getPath($group) . '/' . $file;
|
||||
// add extension if not already part of the file
|
||||
// -> allows switching between extensions (e.g. .jpg, .png) for the same image
|
||||
$link .= empty(pathinfo($file, PATHINFO_EXTENSION)) ? '.' . $this->getFileExtension($group) : '';
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* get resource path
|
||||
* @param string $group
|
||||
* @return string
|
||||
*/
|
||||
public function getPath(string $group) : string {
|
||||
return $this->filePath[$group];
|
||||
}
|
||||
|
||||
/**
|
||||
* build inline HTML <link> tags for resources
|
||||
* @return string
|
||||
*/
|
||||
public function buildLinks(){
|
||||
$this->build();
|
||||
$links = [];
|
||||
foreach($this->resources as $group => $resources){
|
||||
foreach($resources as $file => $conf){
|
||||
$resourceHeader = '<link';
|
||||
foreach($conf['options'] as $attr => $value){
|
||||
$resourceHeader .= ' ' . $attr . '="' . $value . '"';
|
||||
// insert href attr after rel attr -> better readability
|
||||
if($attr == 'rel'){
|
||||
$resourceHeader .= ' href="' . $conf['link'] . '"';
|
||||
}
|
||||
}
|
||||
$links[] = $resourceHeader . '>';
|
||||
}
|
||||
}
|
||||
return "\n\t" . implode("\n\t", $links);
|
||||
}
|
||||
|
||||
/**
|
||||
* build HTTP header for resource preload
|
||||
* -> all registered resources combined in a single header
|
||||
* @link https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/#automatic-push
|
||||
* @return string
|
||||
*/
|
||||
public function buildHeader() : string {
|
||||
$this->build();
|
||||
$headers = [];
|
||||
foreach($this->resources as $group => $resources){
|
||||
foreach($resources as $file => $conf){
|
||||
$resourceHeader = '<' . $conf['link'] . '>';
|
||||
foreach($conf['options'] as $attr => $value){
|
||||
$resourceHeader .= '; ' . $attr . '="' . $value . '"';
|
||||
}
|
||||
$headers[] = $resourceHeader;
|
||||
}
|
||||
}
|
||||
return 'Link: ' . implode(', ', $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* build resource data
|
||||
* -> add missing attributes to resources
|
||||
*/
|
||||
protected function build(){
|
||||
foreach($this->resources as $group => &$resources){
|
||||
foreach($resources as $file => &$conf){
|
||||
if(empty($conf['link'])){
|
||||
$conf['link'] = $this->getLink($group, $file);
|
||||
}
|
||||
|
||||
if( empty($conf['options']['rel']) ){
|
||||
$conf['options']['rel'] = self::ATTR_REL;
|
||||
}
|
||||
if( empty($conf['options']['as']) ){
|
||||
$conf['options']['as'] = $group;
|
||||
}
|
||||
if( empty($conf['options']['type']) && !empty($attrType = $this->getLinkAttrType($group)) ){
|
||||
$conf['options']['type'] = $attrType;
|
||||
}
|
||||
|
||||
if( !empty($additionalAttr = $this->getAdditionalAttrs($group)) ){
|
||||
$conf['options'] = $conf['options'] + $additionalAttr;
|
||||
}
|
||||
}
|
||||
unset($options); // unset ref
|
||||
}
|
||||
unset($resources); // unset ref
|
||||
}
|
||||
|
||||
/**
|
||||
* get 'as' attribute (potential destination) by resource $group
|
||||
* @link https://w3c.github.io/preload/#as-attribute
|
||||
* @param string $group
|
||||
* @return string
|
||||
*/
|
||||
protected function getLinkAttrAs(string $group) : string {
|
||||
return isset(self::ATTR_AS[$group]) ? self::ATTR_AS[$group] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* get 'type' attribute by resource $group
|
||||
* @link https://w3c.github.io/preload/#early-fetch-of-critical-resources
|
||||
* @param string $group
|
||||
* @return string
|
||||
*/
|
||||
protected function getLinkAttrType(string $group) : string {
|
||||
return isset(self::ATTR_TYPE[$group]) ? self::ATTR_TYPE[$group] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* get additional attributes by $group
|
||||
* -> e.g. or fonts
|
||||
* @param string $group
|
||||
* @return array
|
||||
*/
|
||||
protected function getAdditionalAttrs(string $group) : array {
|
||||
return isset(self::ATTR_ADD[$group]) ? self::ATTR_ADD[$group] : [];
|
||||
}
|
||||
|
||||
protected function getFileExtension(string $group) : string {
|
||||
return isset($this->fileExt[$group]) ? $this->fileExt[$group] : '';
|
||||
}
|
||||
}
|
||||
@@ -43,45 +43,28 @@
|
||||
<meta property="og:description" content="PATHFINDER is an 'open source' mapping tool for EVE ONLINE,
|
||||
primarily developed to enrich the gameplay of small scale PvP and PvE.">
|
||||
|
||||
|
||||
<meta name="theme-color" content="#2b2b2b"> {* Chrome, Firefox OS and Opera *}
|
||||
<meta name="msapplication-navbutton-color" content="#2b2b2b"> {* Windows Phone *}
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="#2b2b2b"> {* iOS Safari *}
|
||||
|
||||
<meta name="google-site-verification" content="sHoh0gfMw3x1wiwLTK5OsKsxt7kRgxi69hRgWEGh9DQ" /> {* Youtube verification code *}
|
||||
|
||||
<set pathCSS="{{ @BASE . '/public/css/' . @PATHFINDER.VERSION . '/pathfinder.css' }}" />
|
||||
<set pathJSApp="{{ @BASE . '/' . @tplPathJs . '/app' }}" />
|
||||
<set pathJSRequire="{{ @BASE . '/' . @tplPathJs . '/lib/require.js' }}" />
|
||||
<check if="{{ @tplResource->getOption('output') === 'inline' }}">
|
||||
{{ @tplResource->buildLinks() }} {* Prefetch / Preload *}
|
||||
</check>
|
||||
|
||||
{* Prefetch / Preload *}
|
||||
<link rel="preload" href="{{@pathCSS}}" as="style">
|
||||
<link rel="preload" href="{{@pathJSRequire}}" as="script">
|
||||
<link rel="preload" href="{{@pathJSApp}}.js" as="script">
|
||||
<link rel="preload" href="{{@pathJSApp}}/{{@tplJsView}}.js" as="script">
|
||||
<link rel="prerender" href="//login.eveonline.com">
|
||||
<link rel="dns-prefetch" href="//image.eveonline.com">
|
||||
<link rel="dns-prefetch" href="//i.ytimg.com">
|
||||
|
||||
<check if="{{ @tplJsView != 'mappage' }}">
|
||||
<link rel="prefetch" href="{{@pathJSApp}}/mappage.js" as="script">
|
||||
</check>
|
||||
|
||||
<link rel="preload" href="/public/fonts/oxygen-regular-webfont.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" href="/public/fonts/oxygen-bold-webfont.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
|
||||
<link rel="preload" href="/public/fonts/fa-regular-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" href="/public/fonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" href="/public/fonts/fa-brands-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="{{@pathCSS}}" type="text/css" media="screen">
|
||||
<link rel="stylesheet" href="{{ @tplResource->getLink('style', 'pathfinder') }}" type="text/css" media="screen">
|
||||
</head>
|
||||
<body class="pf-body {{ @tplBodyClass }}" data-js-path="{{ @BASE }}/{{ @tplPathJs }}" data-script="{{ @tplJsView }}" <check if="{{ @tplCharacterId }}">data-character-id="{{ @tplCharacterId }}"</check> data-version="{{ @PATHFINDER.VERSION }}">
|
||||
<body class="pf-body {{ @tplBodyClass }}" data-js-path="{{ @tplResource->getPath('script') }}" data-script="{{ @tplJsView }}" <check if="{{ @tplCharacterId }}">data-character-id="{{ @tplCharacterId }}"</check> data-version="{{ @PATHFINDER.VERSION }}">
|
||||
<include if="{{ @tplPageContent }}" href="{{ @tplPageContent }}"/>
|
||||
|
||||
<!-- Hey dude! Where is all the magic? -->
|
||||
|
||||
<script data-main="{{@pathJSApp}}" src="{{@pathJSRequire}}" ></script>
|
||||
<script data-main="{{ @tplResource->getLink('script', 'app') }}" src="{{ @tplResource->getLink('script', 'lib/require') }}" ></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user