Skip to content
Browse files

first commit of the kiss tool set. much work to do here...

  • Loading branch information...
0 parents commit 57b3c98d2984265d00ac91a557c50256d8dd7120 @Paratron committed Oct 30, 2011
Showing with 1,317 additions and 0 deletions.
  1. +48 −0 README.md
  2. +236 −0 kCacheFly.php
  3. +258 −0 kMySQLi.php
  4. +55 −0 kRequestInfo.php
  5. +309 −0 kTester.php
  6. +220 −0 kTwigBlog.php
  7. +140 −0 kTwitter.php
  8. +51 −0 rescombine.php
48 README.md
@@ -0,0 +1,48 @@
+Kiss Tools
+==========
+
+Introduction
+------------
+This collection of classes has already helped us in many cases.
+These little fellas are ticking in the back of our web-apps and support them with just the features we need.
+
+@TODO: Translate the DocTags into english for some older classes...
+
+These are the contents of the kiss toolset so far:
+
+CacheFly
+--------
+The cachefly class was designed quite a while ago to provide a simple way to add caching to already existing projects.
+
+MySQLi extension
+----------------
+ORM Classes are plain stupid. Get over it.
+I think the mySQLi class provided by php is just fine - mostly.
+This class extends it with a couple of useful methods.
+
+Request Info Collector
+----------------------
+Use this class to easily collect data about your current visitor.
+Which OS is he using? Whats his preferred language? Whats the best matching language that I support?
+With this class, your information is just a function call away.
+
+Unit testing class
+------------------
+Enables you to write unit tests in a totally easy way. The tests are groupable and the class renders the results in beautiful HTML5.
+What do you want more?
+
+Twitter class
+-------------
+Get tweets for a specific search term or tweets of a specific username directly as PHP arrays.
+
+Twig Blog class
+---------------
+Do you think wordpress is just too much? So do I.
+the kTwigBlog is a ridicusly simple blogging engine that relies on the twig templating system.
+
+Resource combiner
+-----------------
+Rescombine packs several CSS or javascript files together with a simple syntax:
+*http://example.com/rescombine.php?files=file1,file2,file3*
+The files are cached and only taken from a specific directory with a specific file extension. Caching is available. Minification is available.
+Use it with mod_rewrite to make requests beautiful: *http://example.com/file1,file2,file3.css*
236 kCacheFly.php
@@ -0,0 +1,236 @@
+<?
+ /**
+ * CacheFly Easy Content Caching System.
+ *
+ * This class helps you to cache dynamic parts of your website which are mostly generated by the same data to reduce database access and serverload.
+ * @author Christian Engel <christian.engel@wearekiss.com>
+ * @version 1.2
+ */
+ class kCacheFly
+ {
+ private $cFolder = "";
+ private $caching = false;
+ private $sqlConnection = null;
+ private $sqlTable = "";
+ private $cacheTimeout = 0;
+ private $noOutput = false;
+ /**
+ * Constructor
+ * @param string $cacheFolder Relative path to the cache folder. It needs to have write access for php - at least 644
+ */
+ function __construct($cacheFolder)
+ {
+ if($cacheFolder != "") $this->cFolder = $cacheFolder; else $this->cError("You have to provide a cache folder");
+ if(!is_writeable($cacheFolder)) $this->cError('Cache folder not accessible. Please set the appropriate file rights');
+ }
+
+ /**
+ * Initialisiert den Cache-Vorgang.
+ *
+ * Wenn das Cache-Objekt nicht verfügbar ist wird der Cache-Recorder gestartet und true zurückgegeben. Wenn das Cache-Objekt verfügbar ist wird dieses eingebunden und es wird false zurückgegeben.
+ * Verwendungsbeispiel:
+ * <code>if($cFly->cache("cache_id"))
+ *{
+ * //Code to Cache...
+ *
+ * $cFly->stop();
+ *}</code>
+ *
+ * <b>Wichtig:</b><br />
+ * Es wird nur gecached was per echo {@link http://de3.php.net/manual/de/function.echo.php} an den Browser weitergegeben würde, die erzeugte Cache-Datei wäre also statisch.<br />
+ * Wenn in einer Cache-Datei Kleinigkeiten dynamisch bleiben sollen verwendet man statt der normalen <? ?> Tags für PHP spezielle Platzhalter für cacheFly: %? ?%.<br />
+ * Beispiel:
+ * <code><?
+ * //Irgendein Code
+ *?>
+ *%?= date("d.m.Y", time()); ?%
+ *<?
+ * //Das war eben eine Ausgabe an den Browser, da die %? ?% Tags von PHP nicht interpretiert werden.
+ *?>
+ *</code>
+ * Die %? ?% Tags werden von cacheFly wieder in korrekte PHP-Tags umgewandelt und in die Cachedatei geschrieben.
+ *
+ * <b>Cachen von reinen Daten, z.B. Arrays</b><br />
+ * Reine Daten sollen nicht beim User angezeigt werden, weshalb man cacheFly anweisen kann seine Daten nicht an den Browser zu schicken, nachdem der Cache-Vorgang abgeschlossen wurde.<br />
+ * Beispiel:
+ *<code><?
+ * if($cFly->cache("eine_cache_id", 0, true))
+ * {
+ * $daten = array("a", "b", "c");
+ * echo serialize($daten);
+ * $cFly->stop();
+ * }
+ *?>
+ *</code>
+ * Dies verhindert die Ausgabe in Textform an den Besucher - das Array wird serialisiert in der Cachedatei abgespeichert.<br />
+ * Auslesen kann man es dann wieder auf folgende Art:
+ *<code>
+ *<?
+ * $daten = unserialize($cFly->getCacheObject("eine_cache_id));
+ *?>
+ *
+ *</code>
+ *
+ * @param string $cacheID Eindeutige ID für das zu verwendende Cache-Objekt. String mit maximal 128 Zeichen, bestehend aus a-z, A-Z, 0-9, _, -, +, !
+ * @param integer $cacheExpires Optionaler Parameter, wann dieses Cache-Objekt seine Gültigkeit verliert und neu erzeugt werden soll. Angabe in Sekunden.
+ * @param boolean $noOutput Optionaler Parameter - Deaktiviert die Ausgabe des Caches an dieser Stelle; nutzbar um lediglich Informationen zu cachen.
+ * @return boolean Gibt true zurück, wenn der Cache inkonsistent ist und erneuert werden muss. Gibt false zurück, wenn der Cache noch gültig ist und das CacheObjekt ausgegeben wurde.
+ */
+ public function cache($cacheID, $cacheExpires = 0, $noOutput = false)
+ {
+ $error = false;
+ if($this->caching) $error = $this->cError("Cache Recorder ist bereits aktiv!");
+ if($cacheID == "") $error = $this->cError("Keine Cache-ID übergeben.");
+ $gueltig = "a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ - + !";
+ $gueltig = explode(" ", $gueltig);
+ if(str_replace($gueltig, "", $cacheID) != "") $error = $this->cError("Ungültige Cache-ID!");
+
+ if(!$error)
+ {
+ $fresh = false;
+
+ if(file_exists($this->cFolder.$cacheID.".php"))
+ {
+ //Okay, CacheFile vorhanden.
+ //Schon abgelaufen, wenn Zeit gegeben wurde?
+ if($cacheExpires > 0)
+ {
+ if(filemtime($this->cFolder.$cacheID.".php") > time() - $cacheExpires) $fresh = true;
+ }
+ else $fresh = true;
+ }
+
+ if($fresh)
+ {
+ if(!$noOutput) include($this->cFolder.$cacheID.".php");
+ flush();
+ return false;
+ }
+ else
+ {
+ $this->caching = $cacheID;
+ $this->cacheTimeout = $cacheExpires;
+ $this->noOutput = $noOutput;
+ ob_start();
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Beendet den Cache-Vorgang. Wird nur bei der Erzeugung eines Cache-Objekts benötigt.
+ * Der Cache-Vorgang wird erst mit dem Aufruf dieser Funktion abgeschlossen.
+ */
+ public function stop()
+ {
+ $error = false;
+ if(!$this->caching) $error = $this->cError("Cache Recorder ist nicht aktiv.");
+
+ if(!$error)
+ {
+ $cacheString = ob_get_contents();
+ ob_end_clean();
+ $suchen = array("<%", "%>");
+ $ersetzen = array("<?", "?>");
+ $cacheString = str_replace($suchen, $ersetzen, $cacheString);
+ $f = fopen($this->cFolder.$this->caching.".php", "w+");
+ fwrite($f, $cacheString);
+ fclose($f);
+
+ if(!$this->noOutput)
+ {
+ include($this->cFolder.$this->caching.".php");
+ flush();
+ }
+
+ $this->caching = false;
+ $this->cacheTimeout = 0;
+
+ }
+ }
+
+ /**
+ * Zerstört das angegebene Cache-Objekt.
+ * @param string $cacheID Eindeutige ID für das zu verwendende Cache-Objekt. String mit maximal 128 Zeichen, bestehend aus a-z, A-Z, 0-9, _, -, +, !
+ * @return boolean
+ */
+ public function clearCacheObject($cacheID)
+ {
+ $error = false;
+ if($cacheID == "") $error = $this->cError("Keine Cache-ID übergeben.");
+ $gueltig = "a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ - + !";
+ $gueltig = explode(" ", $gueltig);
+ if(str_replace($gueltig, "", $cacheID) != "") $error = $this->cError("Ungültige Cache-ID!");
+
+ if(!$error)
+ {
+ if($this->sqlTable != "")
+ {
+ $sql = "DELETE FROM ".$this->sqlTable." WHERE cID = '".$cacheID."' LIMIT 1;";
+ mysql_query($sql);
+ }
+ $result = @unlink($this->cFolder.$cacheID.".php");
+ return $result;
+ }
+ }
+
+ /**
+ * Gibt die Daten eines Cacheobjekts zurück.
+ * Vorsicht - diese Funktion prüft NICHT ob das Objekt noch Gültigkeit hat.
+ *
+ * @param string $cacheID Eindeutige ID für das zu verwendende Cache-Objekt. String mit maximal 128 Zeichen, bestehend aus a-z, A-Z, 0-9, _, -, +, !
+ * @return string|false Gibt entweder den Inhalt des Cache-Objekts zurück, oder false wenn dieses nicht exisitiert.
+ */
+ public function getCacheObject($cacheID)
+ {
+ $error = false;
+ if($cacheID == "") $error = $this->cError("Keine Cache-ID übergeben.");
+ $gueltig = "a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ - + !";
+ $gueltig = explode(" ", $gueltig);
+ if(str_replace($gueltig, "", $cacheID) != "") $error = $this->cError("Ungültige Cache-ID!");
+
+ if(!$error)
+ {
+ if(file_exists($this->cFolder.$cacheID.".php"))
+ {
+ $daten = file_get_contents($this->cFolder.$cacheID.".php");
+ return $daten;
+ }
+ else return false;
+ }
+ else return false;
+ }
+
+ /**
+ * Zerstört alle angelegten Cache-Objekte.
+ * @return boolean
+ */
+ public function clearWholeCache()
+ {
+ if ($handle = opendir($this->cFolder))
+ {
+
+ while (false !== ($file = readdir($handle)))
+ {
+ if(is_file($this->cFolder."/".$file)) unlink($this->cFolder."/".$file);
+ }
+
+ closedir($handle);
+ return true;
+ }
+ else return false;
+ }
+
+ /**
+ * Zeigt eine Fehlermeldung an.
+ * @param string $errorText
+ * @return true
+ */
+ private function cError($errorText)
+ {
+ echo "<h1>CacheFly Error</h1>";
+ echo "<b>".$errorText."</b><br />";
+ return true;
+ }
+ }
+?>
258 kMySQLi.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ * Erweitert die MySQLi-Klasse um zusätzliche Funktionen.
+ * @author Christian Engel <christian.engel@wearekiss.com>
+ * @version 1.31
+ */
+class kMySQLi extends mysqli
+{
+ var $connected = false;
+
+ /**
+ * @var Tabellen-Prefix, der vor Tabellennamen gesetzt werden soll, um schnell auch auf anderen Servern zu arbeiten.
+ */
+ public $prefix = 'tbl_';
+
+ /**
+ * Konstruktor
+ *
+ * @param string $host Hostname des MySQL Servers
+ * @param string $user Username des MySQL Servers
+ * @param string $pass Passwort des MySQL Servers
+ * @param string $db Name der zu verwendenden Datenbank
+ */
+ public function __construct($host, $user, $pass, $db)
+ {
+ parent::__construct($host, $user, $pass, $db);
+ if ( ! $this->connect_error)
+ {
+ $this->connected = TRUE;
+ }
+ }
+
+ /**
+ * Maskiert die Sonderzeichen eines Strings für die MySQL-Übergabe.
+ * @param string $string
+ * @return string
+ */
+ public function mask($string)
+ {
+ if(get_magic_quotes_gpc()) $string = stripslashes($string);
+ return '\''.$this->real_escape_string($string).'\'';
+ }
+
+ public function query($sqlQuery)
+ {
+ $result = parent::query($sqlQuery);
+ echo $this->sql_error;
+ return $result;
+ }
+
+ function insert($sqlQuery){
+
+ }
+
+ /**
+ * Fährt eine Anfrage an die Datenbank und gibt die Insert-ID zurück. (Nicht zur Datenabfrage gedacht).
+ * @param string $sqlQuery
+ * @return integer|false
+ */
+ public function queryInsert($sqlQuery)
+ {
+ $this->query($sqlQuery);
+ if(!$this->error) return $this->insert_id; else return false;
+ }
+
+ /**
+ * Fährt eine Anfrage an die Datenbank und gibt alle Zeilen zurück.
+ * @param string $sqlQuery
+ * @return array|false
+ */
+ public function queryAll($sqlQuery)
+ {
+ $result = $this->query($sqlQuery);
+ if($result)
+ {
+ $ausgabe = array();
+ while($row = $result->fetch_assoc())
+ {
+ $ausgabe[] = $row;
+ }
+ return $ausgabe;
+ }
+ else return array();
+ }
+
+ /**
+ * Fährt eine Anfrage an die Datenbank und gibt eine Zeile zurück.
+ * @param string $sqlQuery
+ * @return array|false Assoziatives Array
+ */
+ public function queryRow($sqlQuery)
+ {
+ $result = $this->query($sqlQuery);
+ if($result)
+ {
+ return $result->fetch_assoc();
+ }
+ else return false;
+ }
+
+ /**
+ * Fährt eine Anfrage an die Datenbank und gibt einen Wert zurück.
+ * @param string $sqlQuery
+ * @return mixed|false
+ */
+ public function queryValue($sqlQuery)
+ {
+ $result = $this->query($sqlQuery);
+ if($result)
+ {
+ $row = $result->fetch_array();
+ return $row[0];
+ }
+ else return false;
+ }
+
+ /**
+ * Erzeugt einen String mit SET-Werten für MySQL.
+ * Strings werden automatisch maskiert.
+ * @param array $array Assoziatives Array. Keys werden als Datenbank-Felder genutzt.
+ * @return string
+ */
+ function makeSqlSetString($array)
+ {
+ $ausgabe = '';
+
+ if(is_array($array))
+ {
+ foreach($array as $key=>$wert)
+ {
+ if($ausgabe != '') $ausgabe .= ', ';
+ if(is_int($wert))
+ {
+ $ausgabe .= $key.'='.$wert;
+ }
+ else
+ {
+ $ausgabe .= $key.'='.$this->mask($wert);
+ }
+ }
+ }
+
+ return $ausgabe;
+ }
+
+ /**
+ * Erzeugt einen String für die Dateneingabe in die Datenbank.
+ * Strings werden automatisch maskiert.
+ * Hat die Fähigkeit fehlende Werte zu interpolieren, wenn Werte im übergebenen Array wiederum Arrays sind.
+ * @param array $array Assoziatives Array
+ * @return string (SPALTEN) VALUES (WERTE)
+ */
+ function makeSqlValueString($array)
+ {
+ $ausgabe = '';
+ $left = array();
+ $right = array();
+ $maxLength = 0;
+
+ //Erstmal vorbereiten.
+ foreach($array as $key => $value)
+ {
+ $left[] = $key;
+ if(is_array($value))
+ {
+ if(count($value)-1 > $maxLength) $maxLength = count($value)-1;
+ }
+ else
+ {
+ $array[$key] = array($value);
+ }
+ }
+
+ //Jetzt die Wertepaare basteln.
+ for($i = 0;$i <= $maxLength;$i++)
+ {
+ $work = array();
+ foreach($array as $key => $value)
+ {
+ if(count($array[$key])-1 < $i)
+ {
+ $wert = $array[$key][count($array[$key])-1];
+ }
+ else
+ {
+ $wert = $array[$key][$i];
+ }
+
+ if(is_int($wert))
+ {
+ $work[] = $wert;
+ }
+ else
+ {
+ $work[] = $this->mask($wert);
+ }
+ }
+ $right[] = implode(', ', $work);
+ }
+
+ $ausgabe = '('.implode(',', $left).') VALUES ('.implode('), (', $right).')';
+
+
+ return $ausgabe;
+ }
+
+ /**
+ * Erzeugt einen Value-Block aus einem Array.
+ * Strings werden automatisch Maskiert.
+ * Arrays oder Objekte werden ignoriert.
+ * @param array $array Assoziatives Array. Keys werden als Datenbank-Felder genutzt.
+ * @return string
+ */
+ function makeSqlValueString_old($array)
+ {
+ $ausgabe = '';
+ $part1 = '';
+ $part2 = '';
+
+ if(is_array($array))
+ {
+ foreach($array as $key=>$wert)
+ {
+ if($part1 != '')
+ {
+ $part1 .= ', ';
+ $part2 .= ', ';
+ }
+ $part1 .= $key;
+
+
+ if(is_int($wert))
+ {
+ $part2 .= $wert;
+ }
+ else if(is_string($wert))
+ {
+ $part2 .= $this->mask($wert);
+ }
+ }
+
+ $ausgabe = '($part1) VALUES ($part2)';
+ }
+
+ return $ausgabe;
+ }
+
+ /**
+ * Leert eine Tabelle.
+ * @param string $tablename
+ * @return void
+ */
+ function clearTable($tablename){
+ $sql = 'TRUNCATE TABLE '.$tablename;
+ $this->query($sql);
+ }
+}
+?>
55 kRequestInfo.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Reads the HTTP Request and gathers some informations about the requester.
+ * @version 1 12.10.2011
+ * @autor Christian Engel <christian.engel@wearekiss.com>
+ */
+class kRequestInfo{
+ /**
+ * Tries to find out which operating system the user is running.
+ * If no operating system could be determined, FALSE is returned.
+ * @return string|FALSE
+ */
+ function os(){
+ if(strpos($_SERVER['HTTP_USER_AGENT'], 'Windows') !== FALSE) return 'win';
+ if(strpos($_SERVER['HTTP_USER_AGENT'], 'Macintosh') !== FALSE) return 'osx';
+ if(strpos($_SERVER['HTTP_USER_AGENT'], 'Linux') !== FALSE) return 'linux';
+ return FALSE;
+ }
+
+ /**
+ * Returns an array with the preferred languages by the user.
+ * The most important language is in index 0.
+ * @param array $available (optional) Pass in here your available languages and the function will only return matches.
+ * @param bool $find_best (optional) If set to TRUE, the first matched language will be returned as string.
+ * @return array|string|FALSE
+ */
+ function preferred_language($available = NULL, $find_best = FALSE){
+ if($available != NULL && !is_array($available)) return FALSE;
+
+ $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
+ $out = array();
+ foreach($langs as $k=>$v){
+ $lang = substr($v, 0, strpos($v, ';'));
+ if($available){
+ if(!in_array($lang, $available)) continue;
+ }
+ if($find_best) return $lang;
+ $out[] = $lang;
+ }
+
+ return $out;
+ }
+
+ /**
+ * This looks if there is a signed request by facebook in the request header and returns the contents.
+ * @return array|false
+ */
+ function signed_request(){
+ if(!isset($_REQUEST['signed_request'])) return FALSE;
+ $r = $_REQUEST['signed_request'];
+ if(!$r) return FALSE;
+ $parts = explode('.', $r);
+ return json_decode(base64_decode($parts[1]), TRUE);
+ }
+}
309 kTester.php
@@ -0,0 +1,309 @@
+<?php
+
+
+class kTester_tests{
+ /**
+ * Tests if $a is equal $b
+ * @param mixed $a
+ * @param mixed $b
+ * @return boolean
+ */
+ function is($a, $b=TRUE){
+ return array(($a == $b), $a.' should have been '.$b);
+ }
+
+ /**
+ * Tests if $a is equal $b and equal in type.
+ * @param mixed $a
+ * @param mixed $b
+ * @return boolean
+ */
+ function is_exact($a, $b=TRUE){
+ return array(($a === $b), $a.' should have been exactly '.$b);
+ }
+
+ /**
+ * Tests if $a is inequal $b.
+ * @param mixed $a
+ * @param bool $b
+ * @return bool
+ */
+ function not($a, $b=FALSE){
+ return array(($a != $b), $a.' should not have been '.$b);
+ }
+
+ /**
+ * Tests if $a is inequal $b and inequal in type.
+ * @param mixed $ab
+ * @param bool $b
+ * @return bool
+ */
+ function not_exact($a, $b=FALSE){
+ return array(($a !== $b), $a.' should exactly not have been '.$b);
+ }
+
+ /**
+ * Tests, if $a is lesser than $b;
+ * @param mixed $a
+ * @param mixed $b
+ * @return boolean
+ */
+ function lt($a, $b){
+ return array(($a < $b), $a.' should be less than '.$b);
+ }
+
+ /**
+ * Tests, if $a is greater than $b;
+ * @param mixed $a
+ * @param mixed $b
+ * @return bool
+ */
+ function gt($a, $b){
+ return array(($a > $b), $a.' should be greater than '.$b);
+ }
+
+ /**
+ * Tests, if $a is less or equal $b
+ * @param mixed $a
+ * @param mixed $b
+ * @return bool
+ */
+ function lte($a, $b){
+ return array(($a <= $b), $a.' should be less than or equal '.$b);
+ }
+
+ /**
+ * Tests, if $a is greater or equal $b
+ * @param mixed $a
+ * @param mixed $b
+ * @return bool
+ */
+ function gte($a, $b){
+ return array(($a >= $b), $a.' should be greater than or equal '.$b);
+ }
+
+ /**
+ * Tests, if $needle is found inside $haystack;
+ * @param mixed $needle
+ * @param array $haystack
+ * @return bool
+ */
+ function in($needle, $haystack){
+ return array(in_array($needle, $haystack), $needle.' should be found inside '.print_r($haystack, TRUE));
+ }
+
+ /**
+ * Tests, if $needle is not found inside $haystack.
+ * @param $needle
+ * @param $haystack
+ * @return bool
+ */
+ function not_in($needle, $haystack){
+ return array(!in_array($needle, $haystack), $needle.' should not be found in '.print_r($haystack, TRUE));
+ }
+
+ /**
+ * Tests, if the number of elements in $in, or the string length of $in
+ * @param array|string $in
+ * @param integer $sum
+ * @return bool
+ */
+ function length($in, $sum){
+ if(is_array($in)) return array((count($in) == $sum), $in.' should have '.$sum.' elements (has '.count($in).')');
+ if(is_string($in)) return array((strlen($in) == $sum), $in.' should have '.$sum.' characters (has '.strlen($in).')');
+ }
+}
+
+/**
+ * Klasse für einfaches Testing.
+ * @autor Christian Engel <christian.engel@wearekiss.com>
+ * @version 1
+ */
+class kTester {
+ private $tests = array();
+ private $tStart = 0;
+ private $totalTests = 0;
+ var $currentTest = 1;
+ private $title = 'kTester - UnitTesting';
+
+ private $totalGroups = 0;
+ private $okGroups = 0;
+ private $failGroups = 0;
+ private $groupSuccess = NULL;
+
+ var $eval;
+
+ function __construct(){
+ $this->eval = new kTester_tests();
+ }
+
+ function __destruct(){
+ $this->render();
+ }
+
+ /**
+ * Startet den Test Vorgang und setzt den test Titel.
+ * @param string $title
+ * @return void
+ */
+ function start($title){
+ ob_start(); //Jede Ausgabe von Daten vermeiden. Wird später bei render() abgefangen.
+ $this->title = $title;
+ $this->tStart = microtime(TRUE);
+ }
+
+ /**
+ * Startet eine neue Testgruppe.
+ * @param string $groupname
+ * @return void
+ */
+ function group($groupname){
+ //Gruppen-Name, Bestandene Tests, Testdetails
+ $this->totalGroups++;
+ if($this->groupSuccess !== NULL){
+ if(!count($this->tests[count($this->tests)-2][2])) $this->groupSuccess = FALSE;
+ if($this->groupSuccess) $this->okGroups++; else $this->failGroups++;
+ }
+ $this->groupSuccess = TRUE;
+ $this->tests[] = array(
+ 'group_title' =>$groupname,
+ 'ok_tests' => 0,
+ 'nearly_tests' => 0,
+ 'failed_tests' => 0,
+ 'tests' => array()); //Enthält die einzelnen Testergebnisse
+ }
+
+ /**
+ * Testet, ob $condition TRUE oder FALSE ist. Wenn FALSE, ist der Test fehlgeschlagen.
+ * @param string $title Name des Tests
+ * @param bool|mixed $condition
+ * @param mixed $expected (optional)
+ * @return bool
+ */
+ function test($title, $evaluation, $additional_data = NULL){
+ $this->currentTest++;
+ $debug_out = ob_get_contents();
+ ob_end_clean();
+
+ if(is_bool($evaluation)) $evaluation = array($evaluation, 'no information avaliable');
+ $this->tests[count($this->tests)-1]['tests'][] = array(
+ 'title' => $title,
+ 'passed' => $evaluation[0],
+ 'pass_info' => $evaluation[1],
+ 'is_optional' => FALSE,
+ 'additional_data' => $additional_data,
+ 'debug_data' => $debug_out);
+ ob_start();
+ return $evaluation[0];
+ }
+
+ /**
+ * This adds an optional Test
+ * @param $title
+ * @param $condition
+ * @param null $expected
+ * @param bool $exact_compare
+ * @return bool
+ */
+ function optest($title, $evaluation){
+ if(is_bool($evaluation)) $evaluation = array($evaluation, 'no information avaliable');
+ $this->tests[count($this->tests)-1]['tests'][] = array(
+ 'title' => $title,
+ 'passed' => $evaluation[0],
+ 'pass_info' => $evaluation[1],
+ 'is_optional' => TRUE);
+ return $evaluation[0];
+ }
+
+ function count_all(){
+ foreach($this->tests as $k=>$v){
+ $groupsuccess = TRUE;
+ $this->totalGroups++;
+ foreach($v['tests'] as $w){
+ $this->totalTests++;
+ if($w['passed']){
+ $this->tests[$k]['ok_tests']++;
+ } else {
+ if($w['is_optional']){
+ $this->tests[$k]['nearly_tests']++;
+ } else {
+ $this->tests[$k]['failed_tests']++;
+ $groupsuccess = FALSE;
+ }
+ }
+ }
+ if($groupsuccess){
+ $this->okGroups++;
+ } else {
+ $this->failGroups++;
+ }
+ }
+ }
+
+ /**
+ * Rendert das Testergebnis in HTML
+ * @return void
+ */
+ function render(){
+ $this->count_all();
+ $tcnt = 1;
+
+ /*echo '<pre>';
+ print_r($this->tests);
+ echo '</pre>'; */
+
+ ?><!DOCTYPE html>
+ <html>
+ <head>
+ <title><?=$this->title?></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link href="lib/php/kTester/kTester.css" rel="stylesheet" type="text/css">
+ <script>
+ function toggle(table_id){
+ var obj = document.getElementById(table_id);
+ if(obj.style.display == 'none'){
+ obj.style.display = 'table';
+ } else {
+ obj.style.display = 'none';
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <h1><?=$this->title?></h1>
+ <p>Total amount of <?=$this->totalTests?> tests run in <?=(microtime(TRUE)-$this->tStart)?> Seconds.</p>
+ <p><?= $this->okGroups ?> of <?= $this->totalGroups ?> Groups are working well. This means there are <b><?=round((100 / $this->totalGroups) * $this->failGroups)?>%</b> of groups left to fix.</p>
+ <? foreach($this->tests as $gruppe):
+ $theid = uniqid();
+ $groupDebug = array();
+ ?>
+ <div class="testgroup <? if(count($gruppe['tests']) == 0) echo 'empty'; else echo ($gruppe['ok_tests'] == count($gruppe['tests'])) ? 'passed' : (($gruppe['failed_tests'] == 0) ? 'nearly' : 'failed') ?>">
+ <h2><?=$gruppe['group_title']?> (<?=$gruppe['ok_tests']+$gruppe['nearly_tests']?>/<?=count($gruppe['tests'])?>) <button onclick="toggle('<?=$theid?>')">Details</button></h2>
+ <table style="display: <?= ($gruppe['ok_tests']+$gruppe['nearly_tests'] == count($gruppe['tests'])) ? 'none' : 'table' ?>;" id="<?=$theid?>">
+ <? foreach($gruppe['tests'] as $test):
+ if($test['debug_data']) $groupDebug[] = array($tcnt, $test['debug_data']);
+ ?>
+ <tr class="<?= ($test['passed']) ? 'passed' : 'failed' ?><?= ($test['is_optional']) ? ' nearly' : '' ?>">
+ <td><?= $tcnt++.' - '.$test['title']?></td>
+ <td><?=($test['passed']) ? 'Passed' : 'Failed => '.$test['pass_info'] ?><?= ($test['is_optional']) ? ' (optional)' : '' ?><? if($test['additional_data']) echo '<br>'.$test['additional_data']?></td>
+ </tr>
+ <? endforeach; ?>
+ </table>
+ </div>
+ <? if(count($groupDebug)): ?>
+ <div class="testgroup debug">
+ <table>
+ <? foreach($groupDebug as $v): ?>
+ <tr>
+ <td>Test <?=$v[0]?></td>
+ <td><pre><?=$v[1]?></pre></td>
+ </tr>
+ <? endforeach; ?>
+ </table>
+ </div>
+ <? endif; ?>
+ <? endforeach; ?>
+ </body>
+ </html><?
+ }
+}
220 kTwigBlog.php
@@ -0,0 +1,220 @@
+<?php
+/**
+ * Easy blog mechanism to be used with the Slim PHP Framework and the TWIG Templating engine.
+ * @autor Christian Engel <christian.engel@wearekiss.com>
+ * @version 1 21.10.2011
+ */
+class kTwigBlog {
+ var $blog_index_file = 'lib/php/cache/blog_index.sa';
+ var $blog_article_template = 'blogpost.twig';
+ var $blog_path = '';
+ var $twig = NULL;
+ var $twig_loader = NULL;
+ var $blog_index = array();
+ var $blog_tags = array();
+
+ var $request_info = array();
+
+ /**
+ * When initiating the kTwigBlog class, there must be passed an twig object for template/article loading purposes and the folder of the blogposts, based on twigs template path.
+ * @param object $twig_object
+ * @param string $blog_path
+ */
+ function __construct($twig_object, $blog_path) {
+ $this->twig =& $twig_object;
+ $this->twig_loader = $twig_object->getLoader();
+ $this->blog_path = $blog_path;
+ }
+
+ private function load_blog_index() {
+ $this->blog_index = unserialize(file_get_contents($this->blog_index_file));
+ }
+
+ /**
+ * This builds the blog index.
+ * @return void
+ */
+ function build() {
+ $blogdir = $this->twig_loader->getPaths();
+ $cutoff = strlen($blogdir[0]) - 1; //Leaving the last slash
+ $blogdir = $blogdir[0] . $this->blog_path;
+ $articles = $this->scan_recursive($blogdir);
+ $this->blog_index = array();
+
+ foreach ($articles as $v) {
+ $template_source = file_get_contents($v);
+
+ $key = trim($this->get_block_content($template_source, 'key'));
+ if (!$key) {
+ die('Cannot build blog index: No key defined in <b>' . $v . '</b>');
+ }
+ if (!isset($this->blog_index[$key])) {
+ $article = array(
+ 'file' => substr($v, $cutoff),
+ 'date' => $this->get_block_content($template_source, 'date'),
+ 'tags' => explode(',', $this->get_block_content($template_source, 'tags'))
+ );
+ $article['unixtime'] = strtotime($article['date']);
+ $this->blog_index[$key] = $article;
+ continue;
+ }
+ die('Cannot build blog index: Key duplicate in <b>' . $v . '</b> and <b>' . $this->blog_index[$key]['file'] . '</b>');
+ }
+
+ uasort($this->blog_index, array($this, 'compare'));
+
+ file_put_contents($this->blog_index_file, serialize($this->blog_index));
+ }
+
+ private function compare($a, $b){
+ if($a['date'] == $b['date']) return 0;
+ return ($a['date'] < $b['date']) ? 1 : -1;
+ }
+
+ /**
+ * Used by build() to find all blogposts.
+ * @param string $folder
+ * @return array
+ */
+ private function scan_recursive($folder) {
+ if (substr($folder, -1) != '/') $folder .= '/';
+ $list = scandir($folder);
+ $result = array();
+ foreach ($list as $v) {
+ if ($v == '.' || $v == '..') continue;
+ if (substr($v, -5) == '.twig') {
+ $result[] = $folder . $v;
+ continue;
+ }
+ if (is_dir($folder . $v)) {
+ $result = array_merge($result, $this->scan_recursive($folder . $v));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Extracts the content of a TWIG block from a template.
+ * @TODO: This will break on nested blocks.
+ * @param string $twig_source
+ * @param string $blockname
+ * @return string|false
+ */
+ private function get_block_content($twig_source, $blockname) {
+ $regex = '#\{% ?block ' . $blockname . '.*?%\}(.*?)\{% ?endblock.*?%\}#ms';
+ preg_match($regex, $twig_source, $matches);
+ if (count($matches) < 1) return FALSE;
+ return $matches[1];
+ }
+
+ /**
+ * This loads, renders and returns a blogpost specified by a key.
+ * Remember to build() the blog before the first call.
+ * Returns boolean FALSE if no article with the defined key was found.
+ * @param string $key
+ * @return string|FALSE
+ */
+ function get_article($key) {
+ if (!count($this->blog_index)) $this->load_blog_index();
+ if (!isset($this->blog_index[$key])) return FALSE;
+ $blogdir = $this->twig_loader->getPaths();
+ $article_src = file_get_contents($blogdir[0] . $this->blog_index[$key]['file']);
+
+ preg_match_all('#\{% ?block (.+?) ?%\}(.*?)\{% ?endblock.*?%\}#ms', $article_src, $matches);
+ $article = array();
+ foreach ($matches[1] as $k => $v) {
+ $article[$v] = $matches[2][$k];
+ }
+ $article['tags'] = explode(',', $article['tags']);
+
+ return $this->twig->loadTemplate($this->blog_article_template)->render(array('article' => $article));
+ }
+
+ /**
+ * Returns all available tags of the built blog.
+ * @return array
+ */
+ function get_tags() {
+ if (!count($this->blog_index)) $this->load_blog_index();
+ if (count($this->blog_tags)) return $this->blog_tags;
+
+ foreach ($this->blog_index as $v) {
+ foreach ($v['tags'] as $x) {
+ if (!isset($this->blog_tags[$x])) {
+ $this->blog_tags[$x] = 1;
+ continue;
+ }
+ $this->blog_tags[$x]++;
+ }
+ }
+
+ $out = array();
+ foreach ($this->blog_tags as $k => $v) {
+ $out[] = array('title' => $k, 'count' => $v);
+ }
+ $this->blog_tags = $out;
+
+ return $this->blog_tags;
+ }
+
+ /**
+ * Returns a List of articles.
+ * If you can specify a tag, the articles must contain.
+ * The List only returns the articles intro texts, title, date, tags and permalink.
+ * @param string $tag (optional)
+ * @param integer $offset (optional) default = 0
+ * @param integer $limit (optional) default = 2
+ * @return array
+ */
+ function get_articles_by_tag($tag = NULL, $offset = 0, $limit = 2) {
+ if (!count($this->blog_index)) $this->load_blog_index();
+ if ($tag == NULL) {
+ $this->request_info = array(
+ 'total_items' => count($this->blog_index),
+ 'total_global_items' => count($this->blog_index),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'more' => (count($this->blog_index) - ($offset + $limit))
+ );
+ return array_slice($this->blog_index, $offset, $limit, TRUE);
+ }
+ $result = array();
+ $tag = strtolower($tag);
+ foreach ($this->blog_index as $k => $v) {
+ $w = strtolower(',' . implode(',', $v['tags']) . ',');
+ if (strpos($w, $tag) !== FALSE) $result[$k] = $v;
+ }
+ $this->request_info = array(
+ 'total_items' => count($result),
+ 'total_global_items' => count($this->blog_index),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'more' => (count($result) - ($offset + $limit))
+ );
+ return array_slice($result, $offset, $limit, TRUE);
+ }
+
+ /**
+ * This function extracts one or more fields of an article.
+ * Use it to generate post indexes.
+ * You may separate your needed fields with commas.
+ * Returns a string if only one field is requested, an array if multiple fields are requested or false if the key was not found.
+ * @param string $key
+ * @param string $fields
+ * @return string|array|FALSE
+ */
+ function get_article_fields($key, $fields) {
+ if (!count($this->blog_index)) $this->load_blog_index();
+ if (!isset($this->blog_index[$key])) return FALSE;
+ if (!$fields) return FALSE;
+ $fields = explode(',', $fields);
+ $blogdir = $this->twig_loader->getPaths();
+ $src = file_get_contents($blogdir[0] . $this->blog_index[$key]['file']);
+ $result = array();
+ foreach ($fields as $field) {
+ $result[$field] = $this->get_block_content($src, $field);
+ }
+ if (count($fields) == 1) return $result[$fields[0]];
+ return $result;
+ }
+}
140 kTwitter.php
@@ -0,0 +1,140 @@
+<?php
+/**
+ * Essencial Twitter functions
+ * @autor Christian Engel <christian.engel@wearekiss.com>
+ * @version 1 16.10.2011
+ */
+class kTwitter {
+ /**
+ * How long should search results be cached? In seconds.
+ * @var int
+ */
+ var $cache_time = 10;
+
+ /**
+ * The folder to put cache files into.
+ * Must obviously be writeable ;)
+ * @var string
+ */
+ var $cache_folder = 'lib/php/cache/';
+
+ /**
+ * Sends a query to the twitter search and returns the result.
+ * Performs some functions to render a better timestamp.
+ * Queries will be cached for the defined $cache_time
+ * @param string $query
+ * @return array
+ */
+ function search($query){
+ if($result = $this->cache_load($query)){
+ return $result;
+ }
+
+ error_reporting(FALSE);
+ $result = json_decode(file_get_contents('http://search.twitter.com/search.json?q='.urlencode($query)), TRUE);
+ if($result == FALSE){
+ return $this->cache_load($query, TRUE);
+ }
+ foreach($result['results'] as $k=>$v){
+ $result['results'][$k]['time'] = strtotime($v['created_at']);
+ $result['results'][$k]['rel_time'] = $this->nice_time_format(strtotime($v['created_at']));
+ }
+ $results['issued_at'] = time();
+
+ $this->cache_write($query, $result);
+ return $result;
+ }
+
+ /**
+ * This function takes an array of tweets either from search() or from_user() and filters the spam out of it.
+ * @param array $tweet_array
+ * @return array
+ */
+ function filter($tweet_array){
+ $result = array();
+ $filterstrings = array(
+ 'If you girls take care',
+ 'BlacksGmgtastic',
+ 'you don\'t like me',
+ 'FUCKING',
+ 'PiegonsAndDucks',
+ 'agora uma vez por seculo'
+ );
+ foreach($tweet_array['results'] as $v){
+ $break = FALSE;
+ foreach($filterstrings as $f){
+ if(strpos($v['text'], $f) !== FALSE){
+ $break = TRUE;
+ continue;
+ }
+ }
+ if(!$break) $result[] = $v;
+ }
+ $tweet_array['results'] = $result;
+ return $tweet_array;
+ }
+
+ function from_user($username){
+ if($result = $this->cache_load('user_'.$username)){
+ return $result;
+ }
+
+ error_reporting(FALSE);
+ $result = json_decode(file_get_contents('http://search.twitter.com/search.json?from='.urlencode($username)), TRUE);
+ if($result == FALSE){
+ return $this->cache_load('user_'.$username, TRUE);
+ }
+ foreach($result['results'] as $k=>$v){
+ $result['results'][$k]['time'] = strtotime($v['created_at']);
+ $result['results'][$k]['rel_time'] = $this->nice_time_format(strtotime($v['created_at']));
+ }
+ $results['issued_at'] = time();
+
+ $this->cache_write('user_'.$username, $result);
+ return $result;
+ }
+
+ /**
+ * This function takes a timestamp and formats it nicely. :)
+ * @param $timestamp
+ * @return string
+ */
+ function nice_time_format($timestamp){
+ $difference = time() - $timestamp;
+
+ if($difference < 60) return 'a few seconds ago';
+ if($difference > 59 && $difference < 120) return 'one minute ago';
+ if($difference > 119 && $difference < 60*60) return round($difference / 60).' minutes ago';
+ if($difference >= 60*60 && $difference < 86400) return round($difference / 3600).' hours ago';
+ if($difference >= 86400 && $difference < 172800) return 'yesterday';
+ if($difference > 172800) return date('jS M Y', $timestamp);
+ }
+
+ /**
+ * Writes data into a cache file.
+ * The identifier string will be turned into an MD5 Hash and used as cache file name.
+ * @param string $identifier
+ * @param mixed $data
+ * @return void
+ */
+ function cache_write($identifier, $data){
+ $file = $this->cache_folder.'twitter_'.md5($identifier);
+ file_put_contents($file, serialize($data));
+ }
+
+ /**
+ * Tries to load a cache file.
+ * If the cache doesnt exist or is invalid, the function will return FALSE.
+ * @param string $identifier
+ * @return mixed|FALSE
+ */
+ function cache_load($identifier, $force = FALSE){
+ $file = $this->cache_folder.'twitter_'.md5($identifier);
+ if(!file_exists($file)) return FALSE;
+ if(!$force){
+ if(filemtime($file) < time()-$this->cache_time) return FALSE;
+ }
+
+ return unserialize(file_get_contents($file));
+ }
+}
51 rescombine.php
@@ -0,0 +1,51 @@
+<?
+
+/**
+ * RESOURCE COMBINER
+ * This script combines multiple CSS or JS files into one single request.
+ *
+ * @autor Christian Engel <christian.engel@wearekiss.com>
+ * @version 1 13.11.2011
+ */
+$nocache = TRUE;
+
+if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
+ ob_start("ob_gzhandler");
+}
+
+$filenames = explode(',', $_GET['files']);
+$content = '';
+
+$parts = explode('?', $_SERVER['REQUEST_URI']);
+$ext = array_shift($parts);
+$ext = substr($ext, strrpos($ext, '.')+1);
+
+switch ($ext) {
+ case 'js':
+ header('Content-Type: text/javascript');
+ break;
+ case 'css':
+ header('Content-Type: text/css');
+ break;
+}
+
+$cachename = md5($ext . $_GET['files']);
+if (file_exists('cache/' . $cachename)) {
+
+ echo file_get_contents('cache/' . $cachename);
+ die();
+}
+
+foreach ($filenames as $file) {
+ if (file_exists('../'.$ext.'/' . $file . '.'.$ext)) {
+ $content .= file_get_contents('../'.$ext.'/' . $file . '.'.$ext);
+ }
+}
+
+$suche = array(' {', ' }', ';}', '{ ', '; ', '; ', "\t", "\n");
+$ersetzen = array('{', '}', '}', '{', ';', ';', '', '');
+$content = str_replace($suche, $ersetzen, $content);
+
+if(!$nocache) file_put_contents('cache/' . $cachename, $content);
+
+echo $content;

0 comments on commit 57b3c98

Please sign in to comment.
Something went wrong with that request. Please try again.