From bf996f7247133c536511c23b6ad30aa222bfd6d9 Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Wed, 15 Apr 2015 21:29:19 +0200 Subject: Initial commit --- include/classes/class.bigpipe.php | 79 +++++++++++ include/classes/class.pagelet.php | 84 ++++++++++++ include/functions.php | 12 ++ index.php | 79 +++++++++++ static/bigpipe-callbacks.js | 27 ++++ static/bigpipe.js | 274 ++++++++++++++++++++++++++++++++++++++ static/blue.php | 17 +++ static/delayJS.php | 16 +++ static/green.php | 17 +++ static/red.php | 17 +++ 10 files changed, 622 insertions(+) create mode 100755 include/classes/class.bigpipe.php create mode 100755 include/classes/class.pagelet.php create mode 100755 include/functions.php create mode 100755 index.php create mode 100755 static/bigpipe-callbacks.js create mode 100755 static/bigpipe.js create mode 100755 static/blue.php create mode 100755 static/delayJS.php create mode 100755 static/green.php create mode 100755 static/red.php diff --git a/include/classes/class.bigpipe.php b/include/classes/class.bigpipe.php new file mode 100755 index 0000000..1edefc8 --- /dev/null +++ b/include/classes/class.bigpipe.php @@ -0,0 +1,79 @@ + $Pagelet->getID(), + 'RESOURCES' => ['CSS' => $Pagelet->getCSSFiles(), 'JS' => $Pagelet->getJSFiles(), 'JS_CODE' => removeLineBreaksAndTabs($Pagelet->getJSCode())] + ]; + + if($last) { + $data['IS_LAST'] = true; + } + + echo ''."\n"; + echo ''."\n\n"; + } + + #==================================================================================================== + # Sendet den Output-Buffer so weit wie möglich in Richtung User + #==================================================================================================== + public static function flushOutputBuffer() { + ob_flush(); flush(); + } + + #==================================================================================================== + # Alle Pagelets an Client schicken + #==================================================================================================== + public static function render($async = FALSE) { + self::flushOutputBuffer(); + + $i = 0; + + ksort(self::$pagelets); + + foreach(array_reverse(self::$pagelets) as $priority => $pagelets) { + foreach($pagelets as $Pagelet) { + if(!self::isEnabled()) { + if($Pagelet->getJSCode()) { + echo ''."\n"; + } + + foreach($Pagelet->getCSSFiles() as $CSSFile) { + echo ''."\n"; + } + + foreach($Pagelet->getJSFiles() as $JSFile) { + echo ''."\n"; + } + } + + else { + self::pageletResponse($Pagelet, $async, (self::$count === ++$i)); + self::flushOutputBuffer(); + } + } + } + } +} \ No newline at end of file diff --git a/include/classes/class.pagelet.php b/include/classes/class.pagelet.php new file mode 100755 index 0000000..c1f211a --- /dev/null +++ b/include/classes/class.pagelet.php @@ -0,0 +1,84 @@ +ID = 'P'.++self::$count; + BigPipe::addPagelet($this, $priority); + } + + #==================================================================================================== + # ID zurückgeben + #==================================================================================================== + public function getID() { + return $this->ID; + } + + #==================================================================================================== + # HTML-Code zurückgeben + #==================================================================================================== + public function getHTML() { + return $this->HTML; + } + + #==================================================================================================== + # CSS-Ressourcen zurückgeben + #==================================================================================================== + public function getCSSFiles() { + return $this->CSSFiles; + } + + #==================================================================================================== + # JS-Ressourcen zurückgeben + #==================================================================================================== + public function getJSFiles() { + return $this->JSFiles; + } + + #==================================================================================================== + # JS-Code zurückgeben + #==================================================================================================== + public function getJSCode() { + return $this->JSCode; + } + + #==================================================================================================== + # HTML-Code hinzufügen + #==================================================================================================== + public function addHTML($HTML) { + $this->HTML .= $HTML; + } + + #==================================================================================================== + # CSS-Ressource hinzufügen + #==================================================================================================== + public function addCSS($file) { + $this->CSSFiles[] = $file; + } + + #==================================================================================================== + # JS-Ressource hinzufügen + #==================================================================================================== + public function addJS($file) { + $this->JSFiles[] = $file; + } + + #==================================================================================================== + # JS-Code hinzufügen + #==================================================================================================== + public function addJSCode($code) { + $this->JSCode .= $code; + } + + #==================================================================================================== + # Magische Methode: __toString() + #==================================================================================================== + public function __toString() { + return '
'.((!BigPipe::isEnabled()) ? $this->getHTML() : NULL).'
'; + } +} \ No newline at end of file diff --git a/include/functions.php b/include/functions.php new file mode 100755 index 0000000..443d594 --- /dev/null +++ b/include/functions.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/index.php b/index.php new file mode 100755 index 0000000..7f3de69 --- /dev/null +++ b/index.php @@ -0,0 +1,79 @@ +addHTML('
I AM JUST A PAGELET WITH RED BACKGROUND
'); +$PageletRed->addCSS('static/red.php'); +$PageletRed->addJS('static/delayJS.php'); +$PageletRed->addJSCode("document.getElementById('red').innerHTML += ' [JS executed]';document.getElementById('red').style.borderRadius = '30px';"); + +#==================================================================================================== +# Pagelet mit blauem Hintergrund +#==================================================================================================== +$PageletBlue = new Pagelet(60); +$PageletBlue->addHTML('
I AM JUST A PAGELET WITH BLUE BACKGROUND
'); +$PageletBlue->addCSS('static/blue.php'); +$PageletBlue->addJS('static/delayJS.php'); +$PageletBlue->addJSCode("document.getElementById('blue').innerHTML += ' [JS executed]';document.getElementById('blue').style.borderRadius = '30px';"); + +#==================================================================================================== +# Pagelet mit grünem Hintergrund +#==================================================================================================== +$PageletGreen = new Pagelet(); +$PageletGreen->addHTML('
I AM JUST A PAGELET WITH GREEN BACKGROUND
'); +$PageletGreen->addCSS('static/green.php'); +$PageletGreen->addJS('static/delayJS.php'); +$PageletGreen->addJSCode("document.getElementById('green').innerHTML += ' [JS executed]';document.getElementById('green').style.borderRadius = '30px';"); +?> + + + + + + + + + + BigPipe Example + + +

BigPipe Example

+

Auf dieser Beispielseite werden insgesamt 3 Pagelets gerendert von denen alle jeweils eine CSS- und eine JS-Ressource haben. Wobei jede der CSS-Ressourcen die Hintergrundfarbe des zugehörigen Pagelets ändert. +BigPipe wird hingehen und diese Pagelets der Reihe nach rendern. Dabei werden zuerst die zugehörigen CSS-Ressourcen geladen und dann der HTML-Code injiziert. Wenn dann von allen Pagelets die CSS-Ressourcen geladen +und der HTML-Code injiziert ist, dann wird BigPipe die JS-Ressourcen der Pagelets einbinden und den statischen Javascript-Code (falls vorhanden) ausführen. Damit man den Pipeline-Effekt auf dieser Beispielseite auch +sieht werden die CSS- und JS-Ressourcen über ein Delayscript geleitet. Debuginformationen findest du in der Javascript-Konsole.

+ +

Weitere Informationen: https://blackphantom.de/artikel/bigpipe-website-pipelining-und-schnellerer-aufbau-durch-einzelne-pagelets/

+ + + + + + \ No newline at end of file diff --git a/static/bigpipe-callbacks.js b/static/bigpipe-callbacks.js new file mode 100755 index 0000000..a9f48f1 --- /dev/null +++ b/static/bigpipe-callbacks.js @@ -0,0 +1,27 @@ +// Folgende Phasen stehen zur Auswahl: PAGELET_STARTED, RESOURCE_DONE, PAGELET_HTML_RENDERED, PAGELET_JS_EXECUTED und BIGPIPE_PAGELETS_RENDERED + +var debugLine = "------------------------------------------------------------------------------------------------------------------------"; + +BigPipe.registerPhaseDoneCallback('PAGELET_STARTED', function(Pagelet) { + console.log(debugLine); + console.log(Pagelet.pageletID + ":\t" + "Die Ausführung des Pagelets wurde gestartet."); +}); + +BigPipe.registerPhaseDoneCallback('RESOURCE_DONE', function(Resource) { + console.log(Resource.pageletID + ":\t" + 'Die Ressource ' + Resource.file + ' wurde geladen.'); +}); + +BigPipe.registerPhaseDoneCallback('PAGELET_HTML_RENDERED', function(Pagelet) { + console.log(Pagelet.pageletID + ":\t" + 'Die Platzhalter wurden mit HTML-Code befüllt.'); +}); + +BigPipe.registerPhaseDoneCallback('BIGPIPE_PAGELETS_RENDERED', function() { + console.log(debugLine); + console.log('BP' + ":\t" + 'Die Platzhalter von allen Pagelets wurden mit ihrem HTML-Code befüllt.'); + console.log(debugLine); +}); + +BigPipe.registerPhaseDoneCallback('PAGELET_JS_EXECUTED', function(Pagelet) { + console.log(Pagelet.pageletID + ":\t" + 'Der zugehörige Javascript-Code des Pagelets wurde ausgeführt.'); + console.log(debugLine); +}); \ No newline at end of file diff --git a/static/bigpipe.js b/static/bigpipe.js new file mode 100755 index 0000000..fcd35e9 --- /dev/null +++ b/static/bigpipe.js @@ -0,0 +1,274 @@ +//=================================================================================================== +// REVEALING MODLUE PATTERN: BigPipe +//=================================================================================================== +var BigPipe = (function() { + //=================================================================================================== + // PROTOTYPE: PageletResource-Konstruktor + //=================================================================================================== + function PageletResource(file, type, pageletID) { + this.pageletID = pageletID; + this.callbacks = []; + this.done = false; + this.file = file; + this.type = type; + } + + //=================================================================================================== + // PROTOTYPE: Startet den Ladevorgang der Ressource + //=================================================================================================== + PageletResource.prototype.start = function() { + if(this.type === 0) { + var element = document.createElement('link'); + element.setAttribute('rel', 'stylesheet'); + element.setAttribute('href', this.file); + } + + else { + var element = document.createElement('script'); + element.setAttribute('src', this.file); + element.async = true; + } + + document.head.appendChild(element); + + element.onload = function() { + BigPipe.executePhaseDoneCallbacks('RESOURCE_DONE', this); + this.executeCallbacks(); + }.bind(this); + + element.onerror = function() { + BigPipe.executePhaseDoneCallbacks('RESOURCE_DONE', this); + this.executeCallbacks(); + }.bind(this); + }; + + //=================================================================================================== + // PROTOTYPE: Registriert eine Callback-Funktion + //=================================================================================================== + PageletResource.prototype.registerCallback = function(callback) { + return this.callbacks.push(callback); + }; + + //=================================================================================================== + // PROTOTYPE: Führt alle registrierten Callback-Funktionen aus + //=================================================================================================== + PageletResource.prototype.executeCallbacks = function() { + if(!this.done) { + this.done = true; + + this.callbacks.forEach(function(callback) { + callback(); + }); + } + }; + + //=================================================================================================== + // PROTOTYPE: Pagelet-Konstruktor + //=================================================================================================== + function Pagelet(data) { + this.pageletID = data.ID; + this.HTML = data.HTML || ""; + this.CSSFiles = data.RESOURCES.CSS; + this.JSFiles = data.RESOURCES.JS; + this.JSCode = data.RESOURCES.JS_CODE; + + this.phase = 0; // 1 => Laden von CSS-Ressourcen, 2 => CSS-Ressourcen geladen, 3 => HTML wurde injiziert, 4 => JS-Ressourcen geladen und JS-Code ausgeführt + this.CSSResources = []; + this.JSResources = []; + } + + //=================================================================================================== + // PROTOTYPE: Startet die Initialisierung des Pagelets und startet die Pagelet-Ressourcen + //=================================================================================================== + Pagelet.prototype.start = function() { + BigPipe.executePhaseDoneCallbacks('PAGELET_STARTED', this); + this.CSSFiles.forEach(function(file) { + this.attachCSSResource(new PageletResource(file, 0, this.pageletID)); + }.bind(this)); + + this.JSFiles.forEach(function(file) { + this.attachJSResource(new PageletResource(file, 1, this.pageletID)); + }.bind(this)); + + this.CSSResources.forEach(function(resource) { + this.phase = 1; + resource.start(); + }.bind(this)); + + if(this.phase === 0) { + this.injectHTML(); + } + }; + + //=================================================================================================== + // PROTOTYPE: Fügt eine CSS-Ressource hinzu + //=================================================================================================== + Pagelet.prototype.attachCSSResource = function(resource) { + resource.registerCallback(this.onloadCSS.bind(this)); + return this.CSSResources.push(resource); + }; + + //=================================================================================================== + // PROTOTYPE: Fügt eine JS-Ressource hinzu + //=================================================================================================== + Pagelet.prototype.attachJSResource = function(resource) { + resource.registerCallback(this.onloadJS.bind(this)); + return this.JSResources.push(resource); + }; + + //=================================================================================================== + // PROTOTYPE: Führt den statischen JS-Code des Pagelets aus + //=================================================================================================== + Pagelet.prototype.executeJSCode = function() { + try { + globalExecution(this.JSCode); + BigPipe.executePhaseDoneCallbacks('PAGELET_JS_EXECUTED', this); + } catch(e) { + console.error(this.pageletID + ":\t" + e); + } + }; + + //=================================================================================================== + // PROTOTYPE: Pagelet-Methode + //=================================================================================================== + Pagelet.prototype.onloadJS = function() { + if(this.phase === 3 && this.JSResources.every(function(resource){ + return resource.done; + })) { + this.executeJSCode(); + this.phase = 4; + } + }; + + //=================================================================================================== + // PROTOTYPE: Pagelet-Methode + //=================================================================================================== + Pagelet.prototype.onloadCSS = function() { + if(this.CSSResources.every(function(resource){ + return resource.done; + })) { + this.injectHTML(); + } + }; + + //=================================================================================================== + // PROTOTYPE: Injiziert den HTML-Code des Pagelets in den DOM + //=================================================================================================== + Pagelet.prototype.injectHTML = function() { + this.phase = 2; + if(placeholder = document.getElementById(this.pageletID)) { + if(this.HTML) { + placeholder.innerHTML = this.HTML; + } + + else { + var content = document.getElementById('_' + this.pageletID); + placeholder.innerHTML = content.innerHTML.substring(5, content.innerHTML.length - 4); + document.body.removeChild(content); + } + } + + this.phase = 3; + + BigPipe.executePhaseDoneCallbacks('PAGELET_HTML_RENDERED', this); + BigPipe.executeNextPagelet(); + + if(BigPipe.phase === 2 && BigPipe.pagelets[BigPipe.pagelets.length - 1].pageletID === this.pageletID) { + BigPipe.executePhaseDoneCallbacks('BIGPIPE_PAGELETS_RENDERED'); + BigPipe.loadJSResources(); + } + }; + + //=================================================================================================== + // BigPipe-Hauptobjekt + //=================================================================================================== + var BigPipe = { + pagelets: [], + phase: 0, // 1 => Erstes Pagelet gestartet, 2 => Alle Pagelets angekommen, 3 => JS-Ressourcen geladen + JS-Code ausgeführt + offset: 0, + phaseDoneCallbacks: {}, + + executeNextPagelet: function() { + if(this.pagelets[this.offset]) { + this.pagelets[this.offset++].start(); + } + + else if(this.phase < 2) { + setTimeout(this.executeNextPagelet.bind(this), 30); + } + }, + + registerPhaseDoneCallback: function(phase, callback) { + if(!this.phaseDoneCallbacks[phase]) { + this.phaseDoneCallbacks[phase] = []; + } + return this.phaseDoneCallbacks[phase].push(callback); + }, + + executePhaseDoneCallbacks: function(phase, param) { + if(this.phaseDoneCallbacks[phase]) { + this.phaseDoneCallbacks[phase].forEach(function(callback) { + callback(param); + }); + } + }, + + onPageletArrive: function(data) { + if(this.pagelets.push(new Pagelet(data)) && this.phase === 0 && !data.IS_LAST) { + this.phase = 1; + this.executeNextPagelet(); + } + + else if(data.IS_LAST) { + this.phase = 2; + if(this.pagelets.length === 1) { + this.executeNextPagelet(); + } + } + }, + + loadJSResources: function() { + this.phase = 3; + var isLoading = false; + + this.pagelets.forEach(function(Pagelet) { + if(Pagelet.JSResources.length === 0) { + Pagelet.onloadJS(); + } + }); + + this.pagelets.forEach(function(Pagelet) { + Pagelet.JSResources.forEach(function(Resource) { + Resource.start(); + isLoading = true; + }); + }); + + if(!isLoading) { + this.pagelets.forEach(function(Pagelet) { + Pagelet.onloadJS(); + }); + } + } + }; + + //=================================================================================================== + // REVEALING MODULE PATTERN: Öffentliche API + //=================================================================================================== + return { + onPageletArrive: function(data) { + BigPipe.onPageletArrive(data); + }, + + registerPhaseDoneCallback: function(phase, callback) { + BigPipe.registerPhaseDoneCallback(phase, callback); + }, + + reset: function() { + BigPipe.phaseDoneCallbacks = {}; + BigPipe.pagelets = []; + BigPipe.offset = 0; + BigPipe.phase = 0; + } + }; +})(); \ No newline at end of file diff --git a/static/blue.php b/static/blue.php new file mode 100755 index 0000000..ddffcf6 --- /dev/null +++ b/static/blue.php @@ -0,0 +1,17 @@ + +#blue{background:blue;} \ No newline at end of file diff --git a/static/delayJS.php b/static/delayJS.php new file mode 100755 index 0000000..1307ca0 --- /dev/null +++ b/static/delayJS.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/static/green.php b/static/green.php new file mode 100755 index 0000000..3e45fc6 --- /dev/null +++ b/static/green.php @@ -0,0 +1,17 @@ + +#green{background:green;} \ No newline at end of file diff --git a/static/red.php b/static/red.php new file mode 100755 index 0000000..9ad993e --- /dev/null +++ b/static/red.php @@ -0,0 +1,17 @@ + +#red{background:red;} \ No newline at end of file -- cgit v1.2.3