PHP-Klasse staticfiles

PHP-Klasse zum Mergen und Cachen von Javascript- und CSS-Dateien im HTML-Header.

Einleitung

Mehrere Dateien im Header (oder allgemein in der Seite) einzubinden bedeutet: der Webbrowser des Besuchers meiner Webseite muss mehr Requests absetzen, bevor er die Seite anzeigen kann.

Ja das kann doch aber minimieren! Die Suchmaschine deiner Wahl liefert mit den Begriffen compress oder minify und css oder js so einige weiterhelfende Treffer.

Und die nachfolgende Klasse ist meine Lösung...
Meine Klasse staticfilesmacht im ersten Schritt Folgendes:

  • sie nimmt alle js und css Dateien entgegen
  • Die Javascripts und CSS Dateien werden zu je einer Datei zusammengefasst. Bei CSS-Dateien werden die ggf. enthaltenen relativen Verweise zu Referenzen mit absolutem Pfad umgeschrieben.
  • unnötige Leerzeichen, Tabs etc. werden entfernt
  • Die zusammengefassten (germergten) Dateien werden als Cachedatei abgelegt. Eine Caching Logik aktualisiert den Cache, sobald die Caching-Zeit abgelaufen ist oder aber sich eine der in derselben Gruppe eingebundenen Dateien geändert hat.
  • Ausgegeben wird im Html-Dokument die Verlinkung zum Cachefile

Updates:

  • 2018-07-23:
    - Fix bei Kompression für JS als auch CSS
  • 2012-10-09:
    - writeMedia Methode erhielt einen 2. Parameter zum Filtern
  • 2012-07-05:
    - fixed: CSS-Default Media Attribut wurde von "screen" auf "all" geändert.
    - added: beim Komprimieren von CSS werden nun auch Kommentare entfernt.
  • 2012-06-30:
    - fixed: im Debugmodus wurde der einleitende Kommentar nicht korrekt geschlossen
    - fixed: Methode addCss nimmt als 2. Parameter nun auch Media-Attribute mit Selektoren auf

Voraussetzung

  • PHP 5
  • Javascript- und Stylesheet-Dateien sind am eigenen Webserver
  • Pfade zu Javascript- und Stylesheet-Dateien erfolgt mit absolutem Pfad
  • Zugriff auf Webserver-Konfiguration zum Aktivieren der gzip Kompression (beim Apache reicht es auch, wenn .htaccess erlaubt ist)

Installation

Download

staticfiles_class_php.zip (19 kB)

Auspacken

Kopieren Sie die Datei staticfiles.class.php in ihr PHP-Verzeichnis. Sie kann auch in einem beliebigen Unterordner abgelegt werden.

Einbindung in deine Webseite

(1)
In deiner PHP-Datei, die den HTML-Header ausgibt, ist die Klasse einmalig einzubinden:

 require_once("/php/staticfiles.class.php"); 
 $o=new Staticfiles();
PHP: staticfiles - Initialisierung



(2)
Es gibt 2 Funktionen, mit denen Javascript- und CSS-Dateien hinzugefügt werden.
Javascript:

 $o->addJs([JS-Dateiname]);
 oder
 $o->addJs([JS-Dateiname], "defer");
PHP: staticfiles - Javascript-Datei hinzufügen



... und die CSS-Dateien:

 $o->addCss([CSS-Dateiname]);
 oder aber
 $o->addCss([JS-Dateiname], [Media-Attribut]);
PHP: staticfiles - CSS Datei hinzufügen



(3)
Die Cachefiles, die alle Dateien zusammenfassen zu erzeugen und die Ausgabe der Links für den HTML-Header erfolgt mit

 $o->writeMedia("js", "execution");
 $o->writeMedia("css");
PHP: staticfiles - Cachedatei erzeugen und HTML-Code ausgeben


und am Ende des Html-Body:

 $o->writeMedia("js", "defer");
PHP: staticfiles - Cachedatei erzeugen und HTML-Code ausgeben

Gzip-Kompression und Caching am Client

Es gibt in der Klasse zwar eine Funktion zur Komprimierung des Codes. Diese entfernt überflüssige Tabs und Leerzeichen. Dies spart ca. (lächerliche) 2% des Codes ein. Dies lässt sich um ca. 70..80% drücken, wenn man die gzip-Kompression auf dem Webserver für .css und .js Dateien aktiviert.

Eine zweite am Webserver notwendige Einstellung ist das Caching am Client. Dies legt fest, wie lange ein Webrowser eine einmal abgerufene Datei im Browsercache festhalten soll.
 
Apache
Nachfolgender Code gilt für den Apache Webserver.
 
# gzip Kompression einschalten:
mod_gzip_on Yes

# Client-Cache aktivieren
ExpiresActive On

### 2 days im Browsercache belassen
ExpiresByType application/x-javascript "access plus 2 days"
ExpiresByType application/javascript "access plus 2 days"
ExpiresByType text/css "access plus 2 days"
Apache Konfiguration; Auszug
 
Nginx
... oder für Nginx:
Die Kompression kann man global für alle Webs in der Server-Sektion einschalten.
 
# gzip Kompression einschalten:
gzip             on;
gzip_types       text/css text/javascript;
Nginx Konfiguration; Auszug


Das Expire lässt sich im im virtuellen Host setzen.
 
location ~ \.(css|js)$ {
    (...)
    # Client-Cache aktivieren
    expires       2d;
}
Nginx Konfiguration; Auszug

Debugging

In der eigenen Entwicklungsumgebung braucht man zumeist die Originaldateien im Zugriff. Bei dieser Klasse kann man sowohl Debugging-Informationen einschalten als auch die Original-Dateien einbinden.
Die Debug Informationen erscheinen im HTML-Code als HTML-Kommentar.

In meiner Entwicklungsumgebung heissen die Hosts local.[domainname] - das fange ich per Matching auf $_SERVER["SERVER_NAME"] ab.

require_once("/php/staticfiles.class.php");
$o=new Staticfiles();

// Test, ob ich in der Entwicklungsumgebung bin
if (strpos($_SERVER["SERVER_NAME"], "local.")===0) {

    $o->setDebugDefaults(); // schaltet Debug Infos ein

    // Paranaoia-Option: wenn man Fehler der Klasse ausschliessen will:
    $o->setMerging(false);  // schaltet Merge aller Dateien aus und bindet Links zu Originalfiles ein
}

// hier geht es weiter mit addJS und addCss ...
PHP: Debugging aktivieren und Originaldateien verwenden

Komplettes Beispiel

Ausgangslage: CSS-Dateien und Javascripts sind im Header des HTML-Dokuments:

<!DOCTYPE html>  
<html>  
    <head>  
        ...  
        <!-- Stylesheets -->  
        <link rel="stylesheet" type="text/css" href="/javascript/DataTables-1.7.6/media/css/demo_table.css" media="screen"></link>  
        <link rel="stylesheet" type="text/css" href="/javascript/DataTables-1.7.6/media/css/demo_page.cs" media="screen"></link>  
        <link rel="stylesheet" type="text/css" href="/javascript/fancybox/jquery.fancybox-1.3.4.css" media="screen"></link>  
        <link rel="stylesheet" type="text/css" href="/css/meine_basisklassen.css" media="screen"></link>  
        <link rel="stylesheet" type="text/css" href="/css/mein.css" media="screen"></link>  
        <link rel="stylesheet" type="text/css" href="/css/mein.css" media="screen and (max-device-width: 700px)"></link>  
        <link rel="stylesheet" type="text/css" href="/css/drucker.css" media="print"></link>  
        ...  
        <!-- Javascripts -->  
        <script type="text/javascript" src="/js/jquery-1.4.3.min.js"></script>  
        <script type="text/javascript" src="/js/jquery.mousewheel-3.0.4.pack.js"></script>  
        <script type="text/javascript" src="/js/jquery.fancybox-1.3.4.pack.js"></script>  
        <script type="text/javascript" src="/js/DataTables-1.7.6/media/js/jquery.dataTables.min.js"></script>  
        <script type="text/javascript" src="/js/meine-eigenen-funktionen.js" defer="defer"></script>  
        <script type="text/javascript" src="/js/nochmehr.js" defer="defer"></script>  
        ...  
    </head>  
    ...  
</html>  

Statt der Link- und Script-Tags werden die Methoden zum Hinzufügen angewendet. Mit writeMedia erfolgt die Ausgabe:

<!DOCTYPE html>  
<html>  
    <head>  
        ...  
        <?php  
  
         // Klasse laden und Objektinstanz erzeugen  
         require_once("/php/staticfiles.class.php");  
         $o=new Staticfiles();  
  
         // CSS hinzufuegen  
         $o->addCss("/javascript/DataTables-1.7.6/media/css/demo_table.css", "screen");  
         $o->addCss("/javascript/DataTables-1.7.6/media/css/demo_page.css", "screen");  
         $o->addCss("/javascript/fancybox/jquery.fancybox-1.3.4.css", "screen");  
         $o->addCss("/css/meine_basisklassen.css", "screen");  
         $o->addCss("/css/mein.css", "screen");  
  
         // CSS mit Selektoren  
         $o->addCss("/css/mobile.css", "screen and (max-device-width: 700px)");  
  
         // CSS zum Drucken  
         $o->addCss("/css/druckausgabe.css", "print");  
  
         // Javascripts hinzufuegen  
         $o->addJs("/js/jquery-1.4.3.min.js");  
         $o->addJs("/js/jquery.mousewheel-3.0.4.pack.js");  
         $o->addJs("/js/DataTables-1.7.6/media/js/jquery.dataTables.min.js");  
         $o->addJs("/js/meine-eigenen-funktionen.js", "defer");  
         $o->addJs("/js/nochmehr.js", "defer");  
  
         // Ausgabe als HTML (Mergen und Kompression werden im Hintergrund ausgefuehrt)  
         $o->writeMedia("css");  
         $o->writeMedia("js", "execution");  
       ?>  
       ...  
    </head>  
    <html>  
       ...  
       <?php  
         $o->writeMedia("js", "defer");  
       ?>  
    </html>  
</html>  

Die Funktionen writeMedia erzeugen/ aktualisieren im Hintergrund die Cachefiles und im Vordergrund wird diese Ausgabe erzeugt:

<-- Ausgabe im Header -->  
<link rel="stylesheet" type="text/css" href="/~cache/css_cache_[...].css" media="screen"></link>  
<link rel="stylesheet" type="text/css" href="/~cache/css_cache_[...].css" media="screen and (max-device-width: 700px)"></link>  
<link rel="stylesheet" type="text/css" href="/~cache/css_cache_[...].css" media="print"></link>  
<script type="text/javascript" src="/~cache/js_cache_[...].js"></script>  
...  
<-- Ausgabe im zuunterst im Dokument -->  
<script type="text/javascript" src="/~cache/js_cache_[...].js" defer="defer"></script>  

Dokumentation

Class: Staticfiles

Methoden verstecken

 __constructpublic __construct()
init function; it sets defaults.
@return bool (dummy)

 _genHtmlprivate _genHtml( $sType, $aFile) ... all param(s) required
generate html code to link the cachefile (or original files if cache is disabled)
@param string $sType type; one of css|js
@param array $aFile array with filedata (keys are attributes)
@return string html code

 _getStatsprivate _getStats( $sOrig, $sCrunched, $sCommment) ... all param(s) required
show some statistics info about code compression an gzip
@param type $sOrig
@param type $sCrunched
@param type $sCommment
@return type

 _logprivate _log( $s) ... all param(s) required
write logging information (only if debug is enabled)
@param type $s
@return type

 addCsspublic addCss( $sFile, $sMedia = 'all', $sTitle = '') ... 1 of 3 param(s) required
add a css file to this object
@param string $sFile path and filename of css file like in href attribute
@param string $sMedia media type
@param title $sTitle title (will be ignored so far)
@return bool (dummy)

 addJspublic addJs( $sFile, $defer = 'execution') ... 1 of 2 param(s) required
add a js file to the js array
@param string $sFile file to load
@param string $sDefer
@return type

 addMediapublic addMedia( $sType, $aFiledata) ... all param(s) required
add media file to array; it is called by addJs() and addCss()
@param string $sType one of css|js
@param array $aFiledata; Keys: file: filename; other: attributes
@return type

 compressCsspublic compressCss( $s) ... all param(s) required
compress css code: remove spaces, tabs, line breaks
@param string $s css data
@return string rewritten css data

 compressJspublic compressJs( $s) ... all param(s) required
compress js code
@param string $s js code
@return string with new js code

 getCacheBasepublic getCacheBase()
get current basepath of cache
@return boolean

 getCacheDirpublic getCacheDir()
get full path of current cache directory
@return boolean

 getDebugpublic getDebug()
get current debug flag
@return boolean

 getMergingpublic getMerging()
get current merge flag
@return boolean

 getMinCacheTimepublic getMinCacheTime()
get current caching time on server
@return integer

 getRewriteCachepublic getRewriteCache()
get current flag to rewrite the cache
@return boolean

 rewriteCsspublic rewriteCss( $s, $sFile) ... all param(s) required
rewrite urls in css data; this function finds relative
pathes and rewrites them to absolute pathes
@param string $s css content
@param string $sFile filename of included css (to detect the "current" path)
@return string the reswritten css code

 setCacheBasepublic setCacheBase( $str) ... all param(s) required
set another basepath (behind webroot)
@param string new basepath
@return boolean

 setCacheDirpublic setCacheDir( $str) ... all param(s) required
set another cache directory.
@param string new basepath (below webroot)
@return boolean

 setDebugpublic setDebug( $bool) ... all param(s) required
set debug flag (for development environment)
@param boolean set true to enable debug output
@return boolean

 setDebugDefaultspublic setDebugDefaults()
set debug default settings
@return bool (always true)

 setDefaultspublic setDefaults()
set all vars to initial values
@return bool (always true)

 setMergingpublic setMerging( $bool) ... all param(s) required
set merge flag (for development environment)
@param boolean set false to disable the generation of merged cachefiles
@return boolean

 setMinCacheTimepublic setMinCacheTime( $int) ... all param(s) required
set minimum cachetime on server for merged js and css files
@param integer value in seconds
@return boolean

 setRewriteCachepublic setRewriteCache( $bool) ... all param(s) required
set flag bRewriteOnEachRequest (for development environment)
@param boolean set true to ignore caching time
@return type

 writeMediapublic writeMedia( $sType, $sMyGroup = false) ... 1 of 2 param(s) required
Merge original files of a group to a single cachefile. It updates or just touches the cachefile.
Output is the html code to include the cached css/ js in the html header.
@param string $sType one of css|js
@param string $sMyGroup filter group (given 2nd parameter in addCss or addJs); i.e. "defer" and "execution" for javascripts or a media attribute for css
@return bool (dummy)

Properties verstecken

$_aStaticprivate $_aStatic =
Array
(
    [js] => Array
        (
            [files] => Array
                (
                )

            [tplHtml] => 
            [fRewrite] => 
            [fCompress] => compressJs
        )

    [css] => Array
        (
            [files] => Array
                (
                )

            [tplHtml] => 
            [fRewrite] => rewriteCss
            [fCompress] => compressCss
        )

)

configuration array
@var array
$bDebugpublic $bDebug = false
flag: show debug output? If it is true then you see the merged files,
replacements and statistic data in the html header section (as comment)
@var boolean
$bMergingpublic $bMerging = false
flag: merge files into a cachefile? If it is false then the source
files will be used. This is helpful in the development environment to
debug code with original files.
@var boolean
$bRewriteOnEachRequestpublic $bRewriteOnEachRequest = false
flag: overwrite cachefile on each request?
You can use it in your development environment. It overrides the setting
of $this->iMinCacheTime and regenerates merged cachefiles on each request.
debug code with original files.
@var boolean
$iMinCacheTimepublic $iMinCacheTime = 3600
minimum time to cache js and css cachefile on server - even if one of the
source files have been changed.
@var integer
$sCacheBasepublic $sCacheBase = /~cache/staticfiles/
name for basepath behind webroot
@var string
$sCacheDirpublic $sCacheDir = false
absolute path of cache directory; it will be set in the method
setDefaults to [document root]/$this->sCacheBase/
@var string