Google PageSpeed Apache module

Het wordt weer eens tijd voor een post, afgelopen dagen wat aan hannesen geweest met google pagespeed optimalisatie en de webservers meteen voorzien van pagespeed_mod. De installatie ging vrij soepel (op Debian, dpkg -i pagespeed.mod.dpkg). De configuratie ging ook bijna vanzelf wat filters hier en daar aanzetten, en klaar 🙂

Maar wat doet deze apache module nu precies? De naam zegt het al het verbeterd de snelheid van het laden van website, hoe doen ze dit dan?

  • Afbeeldingen worden opnieuw gecomprimeerd of omgezet in een andere formaat;
  • Comments overbodige tekens etc worden uit de html, css en javascripts gehaald;
  • Systeem activeert een cache systeem wat zorgdraagt dat de boel snel blijft laden;

Kortom leuke module en zeker zeker zeer handig! Hieronder mijn pagespeed na een uurtje tweaken:

Mijn apache module configuratie:

<IfModule pagespeed_module>

ModPagespeed on
ModPagespeedInheritVHostConfig on
AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html
ModPagespeedFileCachePath            “/var/cache/mod_pagespeed/”
ModPagespeedLogDir “/var/log/pagespeed”
ModPagespeedSslCertDirectory “/etc/ssl/certs”

ModPagespeedRewriteLevel CoreFilters
ModPagespeedEnableFilters prioritize_critical_css
ModPagespeedEnableFilters defer_javascript
ModPagespeedEnableFilters sprite_images
ModPagespeedEnableFilters convert_png_to_webp,convert_jpeg_to_webp
ModPagespeedEnableFilters collapse_whitespace,remove_comments

ModPagespeedFileCacheInodeLimit        500000
ModPagespeedImageRecompressionQuality 60
ModPagespeedJpegRecompressionQuality -1
ModPagespeedWebpRecompressionQuality -1

<Location /pagespeed_admin>

Order allow,deny
Allow from localhost
Allow from 127.0.0.1
SetHandler pagespeed_admin

</Location>

<Location /pagespeed_global_admin>

Order allow,deny
Allow from localhost
Allow from 127.0.0.1
SetHandler pagespeed_global_admin

</Location>

ModPagespeedEnableCachePurge on
ModPagespeedStatisticsLogging on
ModPagespeedMessageBufferSize 100000

</IfModule>

Meer informatie over de module zelf:

https://developers.google.com/speed/pagespeed/module/

Uitleg: Linked Lists in PHP

main-qimg-9c7723e302afb4d0063023eba3055e9e

Linked lists zijn een van de meeste basale dingen die een beetje developer moet weten, een linked list is een lijst met objecten die verwijzen naar het volgende object door middel van pointers (geheugen verwijzingen).

Wat kan een reden zijn om Linked Lists te gebruiken en geen Array:

In een linked list is het makkelijker om nodes toe te voegen tussen bijvoorbeeld 2 nodes, omdat je gewoon een nieuwe node kunt maken en de pointers naar elkaar kunt laten verwijzen. Als jij bijvoorbeeld een array hebt met element 0 tot 9 en je wil na element 4 een nieuwe element op plaats 5 toevoegen moet je de boel opnieuw sorteren en herstructureren (want 5 wordt 6, 6 wordt 7 etc.), dit kost veel meer cpu en geheugen dan met een linked list.

Een andere reden kan zijn, de goede toepasbaarheid in multi threaded applications. Dit omdat je gewoon andere nodes in de lijst kunt verwijderen en tegelijk ook bewerkingen in een object kunt doen zonder dat je een fatale uitzondering krijgt. Als je een array zou gaan her-sorteren en de pointers opnieuw zou toewijzen zou het geheugen van waar de thread bezig is niet meer overeenkomen met waar de array data zich bevindt.

Voorbeeld van een real-life situatie:

Goed bruikbaar in bijvoorbeeld games, of dingen waar veel informatie in een object zit zodat het allemaal een beetje snel blijf 😉 Je kan bijvoorbeeld denken aan Command & Conquer, als je alle objecten moet tekenen op het scherm moet je x,y coordinaten bij houden, doel coordinaten, status voortuig, kleur, type, plaatje of de bitmaps voor het texture mappen etc. Om alle dingen op het scherm te tekenen loop je door de lijst, als het object binnen het kader van het scherm valt teken je hem anders sla je hem over en is die kapot geschoten, unlink je hem gewoon en is die weg 🙂

Hieronder een voorbeeld in php, om, een indruk te krijgen hoe een linked list nu eigenlijk precies werkt.

Output:

LinkedList nodes:
Naam: Victor Link, Leeftijd: 24
Naam: Henk Test, Leeftijd: 70
Naam: Piet Voorbeeld, Leeftijd: 55
Naam: Jan Klaas, Leeftijd: 18
Naam: Sander Aerts, Leeftijd: 34
Naam: Paoter Gustaaf, Leeftijd: 39

Piet Voorbeeld is 55 jaar oud

LinkedList nodes:
Naam: Henk Test, Leeftijd: 70
Naam: Piet Voorbeeld, Leeftijd: 55
Naam: Jan Klaas, Leeftijd: 18
Naam: Sander Aerts, Leeftijd: 34
Naam: Paoter Gustaaf, Leeftijd: 39

LinkedList nodes:
Naam: Henk Test, Leeftijd: 70
Naam: Piet Voorbeeld, Leeftijd: 55
Naam: Jan Klaas, Leeftijd: 18
Naam: Sander Aerts, Leeftijd: 34
/**
	 * **************************************************************************
	 * Linked Lists voorbeeld in php
	 * **************************************************************************
	 * Author			: Sander Aerts (sander@aerts-it.nl)
	 * Created			: 17 - 11 - 2016
	 * Last modified	: 17 - 11 - 2016
	 * **************************************************************************
	*/
	
	/**
	 *  Node object
	 */
	class listItem {
		public $name;
		public $age;
		public $head;
		public $next;
	}
	
	/**
	 *  Eerste node uit de lijst verwijderen
	 */
	function removeFirstItem(&$list)
	{
		/**
		 *  Controle of we niet de enige node zijn in de lijst
		 */
		if ( $list !=null && $list->next != null ) {
			$cNode = $list->next;
			$list  = $cNode;
		} elseif ( $list != null && $list->next == null ) {
			/**
			 *  Enige node, verwijder deze
			 */
			unset($list);
		}
	}
	
	/**
	 *  Laatste node uit de lijst verwijderen
	 */
	function removeLastItem(&$list)
	{
		if ( $list != null ) {
			/**
			 *  Ga naar de laatste node in de lijst
			 */
			$cNode = $list;
			while ( $cNode->next->next != null ) $cNode = $cNode->next;

			/**
			 * Laatste node verwijderen
			 */
			$cNode->next = null;
		}
	}
	
	/**
	 *  Node toevoegen aan begin van de lijst
	 */
	function addItemHead(&$list, $name, $age)
	{
		if ( $list == null ) {
			/* Nog geen nodes in de lijst, maak de eerste node aan  */
			$list 		= new listItem;
			$list->head = true;
			$list->next = null;
			$list->name = $name;
			$list->age  = $age;
		} else {
			/* Nieuwe node toevoegen aan begin van de lijst */
			$newItem 			= new listItem;
			$newItem->head 		= true;
			$newItem->next 		= $list;
			$newItem->name 		= $name;
			$newItem->age  		= $age;
			$list				= $newItem;
			$list->next->head	= false;
		}
	}
	
	/**
	 *  Node toevoegen aan het einde van de lijst
	 */
	function addItemTail(&$list, $name, $age)
	{
		if ( $list == null ) {
			addItemHead($list, $name, $age);
		} else {
			/**
			 *  Ga naar de laatste node in de lijst
			 */
			$cNode = $list;
			while ( $cNode->next != null ) $cNode = $cNode->next;

			/**
			 *  Nieuwe node toevoegen aan begin van de lijst
			 */
			$newItem 			= new listItem;
			$newItem->head 		= false;
			$newItem->next 		= null;
			$newItem->name 		= $name;
			$newItem->age  		= $age;
			$cNode->next		= $newItem;
		}
	}
	
	/**
	 *  Zoek iets in de lijst en resulteer dit resultaat als pointer uit de lijst
	 */
	function searchName(&$list, $name)
	{
		if ( $list != null ) {
			$cNode = $list;
			if ( $cNode->name == $name ) return($cNode);
			while ( $cNode->next != null ) {
				$cNode = $cNode->next;
				if ( $cNode->name == $name ) return($cNode);
			}
		} else {
			return(null);
		}
	}
	
	/**
	 *  Lijst weergeven
	 */
	function showList(&$list)
	{
		echo "\r\nLinkedList nodes:\r\n";
		if ( $list != null ) {
			$cNode = $list;
			echo "Naam: " . $cNode->name . ", Leeftijd: " . $cNode->age . "\r\n";
			while ( $cNode->next != null ) {
				$cNode = $cNode->next;
				echo "Naam: " . $cNode->name . ", Leeftijd: " . $cNode->age . "\r\n";
			}
		} else {
			die("Lijst bevat geen nodes!");
		}
	}
	
	header("Content-type: text/plain");
	
	/**
	 *  Nodes toevoegen aan begin van de lijst, de eerste node wordt
	 *  dus automatisch de 2de node, wat met printen van de lijst dus
	 *  tegenovergestelde volgorde weergeeft zoals hieronder toegevoegd
	 */
	addItemHead($myList, "Sander Aerts", "34");
	addItemHead($myList, "Jan Klaas", "18");
	addItemHead($myList, "Piet Voorbeeld", "55");
	addItemHead($myList, "Henk Test", "70");
	addItemHead($myList, "Victor Link", "24");
	
	/**
	 *  Node toevoegen aan het einde van de lijst, deze komt dus achter de
	 *  node "Sander Aerts"
	 */
	addItemTail($myList, "Paoter Gustaaf", "39");
	
	/**
	 *  Lijst printen
	 */
	showList($myList);
	
	/**
	 *  Zoek naar "Piet Voorbeeld" in de lijst
	 */
	$node = searchName($myList, "Piet Voorbeeld");
	echo "\r\n" . $node->name . " is " . $node->age . " jaar oud\r\n";
	
	/**
	 *  Eerst node verwijderen en lijst printen
	 */
	removeFirstItem($myList);
	showList($myList);
	
	/**
	 *  Laatste node verwijderen en lijst printen
	 */
	removeLastItem($myList);
	showList($myList);

SOAP/REST combinatie API php

Combinatie API zowel RESTful als SOAP, in php met een WSDL

/**
	 *  AOZ Soap Class
	 */
	class soapServiceFunctions { 
		public $authorized 	= false;
		public $testmode	= false;

		/**
		 *  Check auhtorization
		 */
		function authorization($header) {
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			global $_SERVER;
			if ( isset($header->username) && isset($header->hash) ) 
			{ 
				if ( $header->username == "testuser" && $header->hash == "7f3be433c9bab15315e23fbb4664d33c6074211b" ) {
					$this->authorized 			= true;
					$this->testmode				= true;
					$this->userData["userid"] 	= 1;
				} else {
					$this->userData	= $this->auth->checkWSDLAccess($header->username, $header->hash, $_SERVER["REMOTE_ADDR"]);
					if ( $this->userData ) $this->authorized 	= true;
					$this->userData["userid"] 	= $this->userData["id"];
				}
			} 
		} 

		/**
		 *  Exception handler
		 */
		function serviceAPIException($errNr, $msg, $call)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			switch ( $this->serviceType ) {
				case "SOAP":
					$this->soap->fault($errNr, $msg, $call, "Contact the AOZ support department on 076 - 74 10 100.");
				break;;
				default:
					throw new Exception($errNr . "::" . $msg . "::" . $call . "]->" . "Contact the AOZ support department on 076 - 74 10 100.");
				break;;
			}
		}
		
		/**
		 *  Request for bag (basisregistratie adressen information ...
		 */
		function someFunction(
					$firstName,
					$lastName,
		) {	
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			
			return(
				array(
					"processData"				=> $this->serviceConversion($this->procData),
					"aozBagRequest"				=> $this->serviceConversion($rs),
					"dataResources"				=> null	
				)
			);
		}

		/**
		 *  Convert to API 
		 */
		function serviceConversion($dataSet)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			switch ( $this->serviceType ) {
				case "SOAP":
					return(new SoapVar($dataSet, SOAP_ENC_OBJECT));
				break;;
				default:
					return($dataSet);
				break;;
			}
		}
		
		/**
		 *  API Constructor ...
		 */
		function __construct($soapService, $sql, $auth, $crypt, $conversionTbl, $serviceType = "SOAP")
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			global $logData;
			$this->logData			= &$logData;
			$this->soap 			= $soapService;
			$this->sql 				= $sql;
			$this->auth 			= $auth;
			$this->crypt			= $crypt;
			$this->conversionTbl	= $conversionTbl;
			$this->serviceType		= $serviceType;
			$this->logData["API"]	= $serviceType;
			$this->locationImages	= true;
			$this->imageHashes		= array();
			$this->procData			= array(
											"calls"		=> 0,
											"resources"	=> 0,
											"runtime"	=> microtime(true),
											"date"		=> time(),
											"errors"	=> 0,
											"warnings"	=> 0,
											"images"	=> 0,
											"timezone"	=> date_default_timezone_get()
										);
		}
	} 
	
	/**
	 *  RESTful API class
	 */
	abstract class RESTFullAPI extends soapServiceFunctions { 
		/**
		 *  Request vars
		 */
		protected $method		= "";
		protected $endpoint		= "";
		protected $verb			= "";
		protected $args			= Array();
		protected $file			= null;
		protected $username		= "";
		protected $hash			= "";
		
		/**
		 *  API Entrypoint
		 */
		
		/**
		 *  Response function
		 */
		function __response($data, $statusCode = 200)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			header("HTTP/1.1 " . $statusCode . " " . $this->__requestStatus($statusCode));
			return json_encode($data);
		}
		
		/**
		 *  Cleanup input data
		 */
		function __cleanInput($data)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			$clean_input = Array();
			if (is_array($data)) {
				foreach ($data as $k => $v) {
					$clean_input[$k] = $this->__cleanInput($v);
				}
			} else {
				$clean_input = trim(strip_tags($data));
			}
			return $clean_input;
		}
		
		/**
		 *  Check request status
		 */
		function __requestStatus($statusCode)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			$statusCodes = array(
								200 => "OK",
								404 => "Not Found",
								405 => "Method Not Allowed",
								500 => "Internal Server Error"
							);
			return($statusCodes[$statusCode] ? $statusCodes[$statusCode] : $statusCodes[500]);
		}
		
		/**
		 *  RESTful API constructor
		 */
		function __construct($requestInput)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			global $server, $sql, $auth, $crypt, $conversionTbl, $apiClass;
			$apiClass = $this;
			
			/**
			 * Response headers 
			 */
			header("Access-Control-Allow-Orgin: *");
			header("Access-Control-Allow-Methods: *");
			header("Content-Type: application/json");
			
			/**
			 *  Parse arguments
			 */
			$this->args 	= explode("/", rtrim($requestInput));
			$this->endpoint	= array_shift($this->args);
			$this->method	= $_SERVER["REQUEST_METHOD"];
			
			/**
			 *  Check request method
			 */
			if ( $this->method == "POST" && array_key_exists("HTTP_X_HTTP_METHOD", $_SERVER) )
			{
				switch ( $_SERVER["HTTP_X_HTTP_METHOD"] ) {
					case "DELETE":
					case "PUT":
						$this->method = $_SERVER["HTTP_X_HTTP_METHOD"];
					break;;
					default:
						$this->serviceAPIException("400", "Unexpected headers found ...", __CLASS__ . "::" . __FUNCTION__);
					break;;
				}
			}
			
			/**
			 *  Parse data for correct REQUEST method
			 */
			switch ( $this->method ) {
				case "DELETE":
				case "POST":
					$this->request 	= $this->__cleanInput($_POST);
				break;;
				case "GET":
					$this->request 	= $this->__cleanInput($_GET);
				break;;
				case "PUT":
					$this->request 	= $this->__cleanInput($_GET);
					$this->file		= file_get_contents("php://input");
				break;;
				default:
					$this->__response("Invalid method", 405);
				break;;
			}
			
			parent::__construct($server, $sql, $auth, $crypt, $conversionTbl, "RESTFUL");
		}
		
		/**
		 *  Get Arguments from class
		 */
		function getArgs()
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			return($this->args);
		}
		
		function processAPI() {
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			if (method_exists($this, $this->endpoint)) {
				$cc 		= 0;
				$arguments 	= "";
				foreach ($this->args as $var ) {
					$varList[$cc] = $var;
					$arguments .= "\$varList[$cc], ";
					$cc++;
				}
				
				$reflection = new ReflectionMethod('soapServiceClass', $this->endpoint);
				if ( $reflection->getNumberOfRequiredParameters() > $cc ) {
					$this->serviceAPIException("428", "Required arguments missing ...", __CLASS__ . "::" . __FUNCTION__);
				} else {
					$arguments  = substr($arguments, 0, strlen($arguments) - 2);
					$call 		= "\$result = \$this->{\$this->endpoint}(" . $arguments . ");";
					eval($call);
					return $this->__response($result);
				}
			}
			return $this->__response("No Endpoint: $this->endpoint", 404);
		}
		
		/**
		 *  RESTful API deconstructor
		 */
		function __deconstruct()
		{
			
		}
	}
	
	/**
	 *  This RESTful API entrypoint
	 */
	class RESTApi extends RESTFullAPI {		
		/**
		 *  API Constructor
		 */
		function __construct($request, $origin, $start)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			$request = strstr($request, $start);
			parent::__construct($request);
			
			if ( isset($this->request["username"]) && isset($this->request["hash"]) ) 
			{ 
				/**
				 *  Are we a test user?
				 */
				if ( $this->request["username"] == "testuser" && $this->request["hash"] == "7f3be433c9bab15315e23fbb4664d33c6074211b" ) {
					$this->authorized 			= true;
					$this->testmode				= true;
					$this->userData["userid"] 	= 1;
				} else {
					/**
					 *  Check user WSDL access
					 */
					$this->userData	= $this->auth->checkWSDLAccess($this->request["username"], $this->request["hash"], $_SERVER["REMOTE_ADDR"]);
					if ( $this->userData ) $this->authorized 	= true;
					$this->userData["userid"] 	= $this->userData["id"];
				}
			} else {
				$this->serviceAPIException("400", "Missing authorization parameters ...", __CLASS__ . "::" . __FUNCTION__);
			}

		}
	}
	
	/**
	 *  Check API request type, RESTful / SOAP
	 */
	$requestHeaders 		= getallheaders();
	$logData["headers"]		= json_encode($requestHeaders);
	$logData["url"]			= $_SERVER["REQUEST_URI"];
	$logData["rawRequest"]	= file_get_contents("php://input");
	$logData["ip"]			= $_SERVER["REMOTE_ADDR"];
	
	/**
	 *  API Processing
	 */
	if ( array_key_exists("SOAPAction", $requestHeaders) || isset($_GET["wsdl"]) ) {	
		/**
		 *  SOAP/API Service Entrypoint
		 */
		if ( isset($_GET["wsdl"]) ) {
			header("Content-type: text/xml");
			/**
			 *  AOZ_WSE_ENDPOINT_PEOPLE_SEARCH Set service endpoint
			 */
			echo str_replace("%%AOZ_WSE_ENDPOINT_PEOPLE_SEARCH%%", AOZ_WSE_ENDPOINT_PEOPLE_SEARCH, file_get_contents("aozWse.wsdl"));
		} else {
			$soapServiceOptions = array(
									"soap_version"	=> SOAP_1_2,
									"cache_wsdl"	=> WSDL_CACHE_NONE,
									"send_errors"	=> true
									);
			$server = new SoapServer("aozWse.wsdl", $soapServiceOptions);
			$server->setClass("soapServiceClass", $server, $sql, $auth, $crypt, $conversionTbl);
			$server->handle();
		}
		$logData["vars"]		= "";
	} else {
		/**
		 *  RESTful API Service Entrypoint
		 */
		if ( array_key_exists("request", $_REQUEST) ) {
			if (!array_key_exists('HTTP_ORIGIN', $_SERVER)) {
				$_SERVER['HTTP_ORIGIN'] = $_SERVER['SERVER_NAME'];
			}

			try {
				$API = new RESTApi($_REQUEST['request'], $_SERVER['HTTP_ORIGIN'], $_REQUEST['start']);
				echo $API->processAPI();
				$logData["vars"] = json_encode($API->getArgs());
			} catch (Exception $e) {
				echo json_encode(Array('error' => $e->getMessage()));
			}
		} else {
			echo "RESTful API Endpoint";
		}
	}