Side Projects:

Facedetection
Source Magic
Intrusion Detection System
Issue Tracker
Searchengine
RemindMe
Open API

Impressum
Andreas Beder 2012


Realtime bandwidth meter with php and jquery

 

Download

SourceCode of data.php

Code

<?
        
    $int
="eth0";
    
session_start();
    
    
$rx[] = @file_get_contents("/sys/class/net/$int/statistics/rx_bytes");
    
$tx[] = @file_get_contents("/sys/class/net/$int/statistics/tx_bytes");
    
sleep(1);
    
$rx[] = @file_get_contents("/sys/class/net/$int/statistics/rx_bytes");
    
$tx[] = @file_get_contents("/sys/class/net/$int/statistics/tx_bytes");
    
    
$tbps $tx[1] - $tx[0];
    
$rbps $rx[1] - $rx[0];
    
    
$round_rx=round($rbps/10242);
    
$round_tx=round($tbps/10242);
    
    
$time=date("U")."000";
    
$_SESSION['rx'][] = "[$time$round_rx]";
    
$_SESSION['tx'][] = "[$time$round_tx]";
    
$data['label'] = $int;
    
$data['data'] = $_SESSION['rx'];
    
# to make sure that the graph shows only the
    # last minute (saves some bandwitch to)
    
if (count($_SESSION['rx'])>60)
    {
    
    $x min(array_keys($_SESSION['rx']));
    
    unset($_SESSION['rx'][$x]);
    }
    
    
# json_encode didnt work, if you found a workarround pls write me
    # echo json_encode($data, JSON_FORCE_OBJECT);
    
    
echo '
    {"label":"'
.$int.'","data":['.implode($_SESSION['rx'], ",").']}
    '
;
    
?>

Source of index.php

Code

<?php
    session_start
();
    
session_destroy();
    
session_start();
    
?>
    <html>
        <head>
            <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
            <script type="text/javascript" src="js/jquery.flot.js"></script>
            
            <script id="source" language="javascript" type="text/javascript">
            $(document).ready(function() {
                var options = {
                    lines: { show: true },
                    points: { show: true },
                    xaxis: { mode: "time" }
                };
                var data = [];
                var placeholder = $("#placeholder");
    
                $.plot(placeholder, data, options);
    
                var iteration = 0;
    
                function fetchData() {
                    ++iteration;
    
                    function onDataReceived(series) {
                        // we get all the data in one go, if we only got partial
                        // data, we could merge it with what we already got
                        data = [ series ];
                        
                        $.plot($("#placeholder"), data, options);
                        fetchData();
                    }
    
                    $.ajax({
                        url: "data.php",
                        method: 'GET',
                        dataType: 'json',
                        success: onDataReceived
                    });
                    
                }
    
                setTimeout(fetchData, 1000);
            });
    
        </script>
        </head>
        <body>
        <div id="placeholder" style="width:600px;height:300px;"></div>
        </body>
    </html>


Kommentare

Klassen von PHP erstellen lassen

In der Regel programmieren wir ja, um anderen die Arbeit leichter zu machen, bzw. erst zu ermöglichen.
Heute möchte ich einen Weg vorstellen, wie wir uns das Leben etwas leichter machen können.
Ich habe dazu zwei Klassen Online gestellt ClassCreater und TemplateCreater.
Wie der Name schon sagt, erstellt dieses Projekt automatisch Klassen und das dazugehörige Template.
So enthält die damit generierte Klasse alle nötigen Methoden um Daten
in einer Datenbank anzulegen, zu bearbeiten und zu löschen.

Das Projekt befindet sich zwar noch im Entwicklungsstadium, dennoch ist der generierte PHP Code
bereits voll funktionstüchtig.

Die Klassen sowie das Template werden anhand eines create table statements generiert->

BSP:

create table contact (
    id int(12) unsigned not null auto_increment,
    firstname varchar(255) default null,
    lastname varchar(255) default null,
    email varchar(255) default null,
    birthday datetime NOT NULL default '0000-00-00 00:00:00',
    comment text,
    PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


Und dazu passend hier die generierte Klasse:
 

Update: Die Security issues sollten jetzt behoben sein !

Code

<?php
    
    
class contact extends BaseApp {
    
    
    
    /**
         * @var int
         */
    
    public $id 0;
    
    
    /**
         * @var string
         */
    
    public $firstname '';
    
    
    /**
         * @var string
         */
    
    public $lastname '';
    
    
    /**
         * @var string
         */
    
    public $email '';
    
    
    /**
         * @var string
         */
    
    public $birthday '';
    
    
    /**
         * @var string
         */
    
    public $comment '';
    
    
    protected $db;
    
    
        
        
/**
         * The constructer
         */
    
    public function __construct ($id '')
    
    {
    
        if (!isset($this->db))
    
           $this->db parent::getInstance();
    
    
        if (!empty($id))
    
        {
    
           $this->id $id;
    
           $this->get();
    
        }
    
    }
    
    
    
    /**
         * Sanitize a input array
         *
         * @param array $data
         * @return array
         */
    
    public function check($data '')
    
    {
    
        $check = array(
    
        'id' => FILTER_SANITIZE_NUMBER_INT,
    
        'firstname' => FILTER_SANITIZE_STRING,
    
        'lastname' => FILTER_SANITIZE_STRING,
    
        'email' => FILTER_SANITIZE_STRING,
    
        'birthday' => FILTER_SANITIZE_STRING,
    
        'comment' => FILTER_SANITIZE_STRING
            
);
    
        return filter_var_array($data$check);
    
    }
    
    
    
    /**
         * Delete a element in the Database
         */
    
    public function del ()
    
    {
    
        if (empty($this->id) || !is_numeric($this->id))
    
            return false;
    
    
        $sql "DELETE FROM contact WHERE id = :id";
    
        $stmt $this->db->dbh->prepare($sql);
    
        $stmt->bindValue(':id'$this->idPDO::PARAM_INT);
    
        $stmt->execute();
    
    }
    
    
    
    /**
         * Get a list of objects
         *
         * @param array $data optional if you like to get only a subset of data
         *
         * @return obj
         */
    
    public function get_list ($data '')
    
    {
    
        $sql "SELECT c.id, c.firstname, c.lastname, c.email, c.birthday, c.comment FROM contact as c";
    
        if (is_array($data))
    
        {
    
            $data $this->check($data);
    
            $sql .= " WHERE";
    
            foreach ($data as $key => $value)
    
            {
    
                if ($value)
    
                    $sql .= " c.$key = '$value' and";
    
            }
    
            $sql substr($sql0, -4);
    
        }
    
        $stmt $this->db->dbh->query($sql);
    
        $obj $stmt->fetchALL(PDO::FETCH_CLASS'contact');
    
        return $obj;
    
    
    }
    
    
    
    /**
         * Get one Dataset from the db
         */
    
    public function get ()
    
    {
    
        if (empty($this->id) || !is_numeric($this->id))
    
            return false;
    
    
        $sql "SELECT c.id, c.firstname, c.lastname, c.email, c.birthday, c.comment FROM contact as c WHERE
            c.id = 
$this->id";
    
        $stmt $this->db->dbh->query($sql);
    
        $result $stmt->fetch(PDO::FETCH_ASSOC);
    
        foreach ($result as $key=>$val)
    
        {
    
            $this->$key=$val;
    
        }
    
    }
    
    
    
    /**
         * The save method is responsability for
         * saving and updating a dataset
         */
    
    public function save ()
    
    {
    
        if (empty($this->id))
    
        {
    
            $sql 'INSERT INTO contact  (firstname, lastname, email, birthday, comment) VALUES
                (:firstname, :lastname, :email, :birthday, :comment)'
;
    
            $stmt $this->db->dbh->prepare($sql);
    
        }
    
        else
    
        {
    
            $sql 'UPDATE contact set
                 firstname = :firstname,
                 lastname = :lastname,
                 email = :email,
                 birthday = :birthday,
                 comment = :comment'
;
    
            $sql .= " WHERE id = :id";
    
            $stmt $this->db->dbh->prepare($sql);
    
            $stmt->bindValue(':id'$this->idPDO::PARAM_INT);
    
        }
    
        $stmt->bindValue(':firstname'$this->firstnamePDO::PARAM_STR);
    
        $stmt->bindValue(':lastname'$this->lastnamePDO::PARAM_STR);
    
        $stmt->bindValue(':email'$this->emailPDO::PARAM_STR);
    
        $stmt->bindValue(':birthday'$this->birthdayPDO::PARAM_STR);
    
        $stmt->bindValue(':comment'$this->commentPDO::PARAM_STR);
    
        $stmt->execute();
    
    }
    }
    
?>


Ich denke das die automatisch generierte Klasse relativ übersichtlich und verständlich gehalten ist,
dazu passend hab ich auch noch ein kleines Bsp. Script wie man nun diese Klasse benutzen kann.

Code
<?
    
include('./inc/class/BaseApp.php');
    include(
'./inc/class/contact.php');
    
    
# ein eintrag erstellen
    
$new_contact = new contact();
    
$new_contact->firstname "andreas";
    
$new_contact->save();
    
    
# ein eintrag aktualisieren
    
$contact = new contact(1);
    
$contact->firstname "andreas";
    
$contact->lastname "beder";
    
$contact->save();
    
    
# nun laden wir eine liste, mit gewissen eigenschaften
    
$list $contact->get_list(array("firstname" => "andreas""lastname" => "beder"));
    foreach(
$list as $obj)
    {
    
    # das aktualisieren geht ganz einfach
    
    $obj->email="andreas@codejungle.org";
    
    $obj->save();
    
    
    # ebenso das löschen
    
    $obj->del();
    }
    
    
?>
    


Wer es selber mal ausprobieren oder sich daran beteiligen mag, kann sich das Projekt hier herunterladen:

svn co svn://codejungle.org/sourcemagic

Das Projekt ist wie gesagt noch nicht fertig, so benötigt das generieren von HTML Templates noch etwas mehr Zuwendung.
Ich hoffe aber, dass ich das in den kommenden Wochen und vielleicht mit deiner Hilfe in den Griff bekomme.

An dieser Stelle möchte ich auch noch die bekannten Object-Relational Mapper erwähnen,
Doctrine und Propel, welche beide sehr umfangreiche Funktionen zur Verfügung stellen.
Wer mehrere Tabellen verbinden möchte und komplexe Anfragen bewältigen muss ist hier gut bedient.

Man sollte allerdings immer bedenken, dass bei Applikationen, die sehr hohe Last bewältigen müssen, ein zusätzlicher layer
meistens mehr Komplexität bedeutet und ausserdem oft auf Kosten der Performance geht.

Mein Projekt bietet im Vergleich zu Doctrine und Propel wesentlich weniger Funktionen,
bei mir werden einfache C.R.U.D (Create, Read, Update und Delete) Methoden automatisch generiert,
was im wesentlichen uns Programmierern etwas Schreibarbeit abnehmen soll, die Applikationslogik
müssen wir natürlich weiterhin selbst erstellen.

Wer mit mir das Projekt weiterentwicklen möchte, schreibt am Besten einfach eine kurze Mail, ich werde dann
bei Bedarf SVN Zugang zur Verfügung stellen.

Ich freue mich natürlich auch über Verbesserungsvorschläge und Kommentare.

Happy Hacking

Andreas


Kommentare

Lastenverteilung mit PHP

Ich habe in einem Artikel von der Möglichkeit geschrieben mit
MySQL-Proxy eine lastenverteilte Datenbank aufzubauen.

Heute möchte ich jedoch eine andere Alternative vorstellen.
Ich habe zu diesem Zweck eine Datenbank Lastenverteilungsklasse geschrieben.
Sie stellt drei Methoden zur Lastenverteilung bereit:

Simple Load Balancing verteilt die Last völlig willkürlich:
Vorteil: relativ schnell
Nachteil: Server können einseitig belastet werden

Load Balancing by Weight verteilt die Last nach Gewichtung:
Hat man z.B. eine starke und zwei schwache Maschinen
ist es möglich der starken mehr Gewichtung zu geben.
Nachteil: Der richtige Gewichtungswert von Maschinen ist schwer zu ermittelen
und kann sich im regulären Betrieb unerwartet ändern.

Load Balancing by Load verteilt die Last nach Systemauslastung:
Vorteil: Die Last wird an den Server verteilt, der am wenigsten zu tun hat.
Nachteil: Die zusätzliche Zeit, die benötigt wird, um die Systemlast der Server abzufragen.
Läuft nur unter Linux, benötigt memcache.

Derzeit wird die Systemlast 60 Sekunden im memcache zwischengespeichert um unnötige Anfragen
an den Webserver zu sparen,
evtl. muss dieser Wert angepasst werden, damit unter hoher Last die Maschinen nicht einseitig
belastet werden.


Desweiteren stellt die Klasse eine Methode zum Abfragen der durchschnittlichen Systemlast bereit
und ausserdem eine Methode die endscheiden kann, ob eine Abfrage auf den Master oder Slave laufen soll.


Code


<?
/**
 * DBLoadBalancing implements three different methods for LoadBalancing
 * and one Method for Read / Write Splitting
 *
 * Simple Load Balancing - random dissision
 * Load Balancing by Weight - dissision by weight
 * Load Balancing by Load - dissision by load
 *
 * @todo benchmark and test bevore useing it on productive system
 * @author Andreas Beder <office@codejungle.org>
 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
 */
class DBLoadBalancing{

    var 
$config =array();

    function 
__construct() {

        
//Config for Simple Load Balanncing

        
$this->config['slaves']=array("10.0.0.1","10.0.0.2""10.0.0.3");
        
/*
        //Config for Load Balancing by Weight

        $this->config['slaves']=array("10.0.0.1"=>2,"10.0.0.2"=>0, "10.0.0.3"=>1);

        //Config for Load Balancing by Load

        $this->config['slaves']=array(
        "10.0.0.1"=>"http://slave1/getload.php",
        "10.0.0.2"=>"http://slave2/getload.php",
        "10.0.0.3"=>"http://slave3/getload.php"
        );
        $this->config['memcache']=array(
        "localhost"=>"11211",
        "slave2", "11211"
        );
        */
    
}

    
/**
     * This Method checks if a Query
     * can be run on Master or Slaves
     *
     * @param string $sql
     * @return boolen
     *
     * @author Andreas Beder <office@codejungle.org>
     */

    
function ModifingQuery($sql){

        
$sql=strtolower(substr($sql06));

        switch (
$sql) {

            
//selects can be run on slaves, because it's not a modifiny query
            
case "select":
                return 
false;
                break;
            
//insert, update, delete, alter must be executed on master
            
case "default":
                return 
true;
        }
        return 
true;
    }

    
/**
     * Simple Load Balanncing
     * Just random Load Balancing
     *
     * @return string ip
     *
     * @author Andreas Beder <office@codejungle.org>
     */
    
function SimpleLoadBalanncing(){

        
$i=count($this->config['slaves'])-1;
        
$rand=rand(0$i);
        return 
$this->config['slaves'][$rand];
    }

    
/**
     * Load Balancing by Weight
     *
     * @return string ip
     *
     * @author Andreas Beder <office@codejungle.org>
     */
    
function LoadBalancingByWeight(){

        
$i=count($this->config['slaves']);
        foreach(
$this->config['slaves'] as $server => $weight){
            
$rand=rand(1$i);
            
$lb[$server]=$rand+$weight;
        }
        
arsort($lb);
        return 
key($lb);

    }

    
/**
     * LoadBalancingByLoad
     *
     * This method checks the load on n server.
     * Cache the result for 60 sec (because we don't want to ask too many times)
     * Finaly it returns the Server ip with the lowest load
     *
     * @return string ip
     *
     * @author Andreas Beder <office@codejungle.org>
     */
    
function LoadBalancingByLoad() {

        
$memcache = new Memcache;
        foreach(
$this->config['memcache'] as $memcahe_server => $port){
            
$memcache->addServer($memcahe_server$port);
        }

        foreach(
$this->config['slaves'] as $server => $checkloadurl){
            if(
$memcache->get($server)){
                
$load[$server]=$memcache->get($server);
            }
            else
            {
                
$curl curl_init();
                
curl_setopt($curlCURLOPT_URL$checkloadurl);
                
curl_setopt($curlCURLOPT_RETURNTRANSFER1);
                
$output curl_exec($curl);
                
$memcache->set($memcache$server$output060);
                
$load[$server]=$output;
                
curl_close($curl);
            }
        }
        
asort($load);
        return 
key($load);
    }

    
/**
     * Get Load simply returns the laod average
     * from the last minute
     *
     * @return float load avg
     *
     * @author Andreas Beder <office@codejungle.org>
     */
    
function GetLoad(){
        
$load=sys_getloadavg();
        return 
$load[0];
    }
}

?>


Ich habe versucht die Methoden so generisch zu schreiben, dass man sie auch für andere
Lastenverteilung nutzen kann.
Es ist ebenso denkbar, gewisse Methoden zu kombinieren.

Ich hoffe euch hat mein kleiner Artikel zur Lastenverteilung gefallen,
über Feedback würd' ich mich wie immer sehr freuen.

Eine Warnung noch zum Schluss, bitte verwendet die Klasse nicht ohne gezieltes
Benchmarking und Testing im Produktivsystem.


LG

Andreas


Kommentare

Die PHP Performance Tage auf Codejungle.org Part 1

Ich habe mir gedacht, nachdem ich ja sonst nix zu tun habe, ein paar
Tage lang eine bestehende Applikation so umzuschreiben, dass
sie auf mehreren Servern läuft.
Als Basis werde ich meinen Reminder Service verwenden.

Der erste Schritt um die Performance zu verbessern ist immer
die Analyse der bestehenden Applikation, dazu gehört auch das Profiling, um
Bottlenecks besser zu lokalisieren.

Um die Skalierbarkeit zu verbessern, werde ich
gewisse Teile ausgliedern, man nennt dies auch funktionale
Partitionierung.
Man könnte bei einem typischen Blog zum Beispiel anfangen,
Kommentare, Benutzer und Nachrichten jeweils auf eine eigene Datenbank auszulagern.
Weiters könnte man mit Hilfe eines Lastenverteilers und mehreren Webservern die
Last aufteilen.

In unserem Beispiel haben wir zwei Kernbereiche, erstens das Userinterface,
worüber der Benutzer Daten anlegen, verändern und löschen kann und
zweitens das Backend, welches die eingegebenen Daten verarbeitet.

Im Idealfall können wir am Ende das Front und Backend auf N Servern horizontal skalieren.
Das bedeutet, wir erhöhen nicht nur die Geschwindigkeit, sondern schaffen zusätzlich Redundanzen,
die eine gewisse Ausfallsicherheit garantieren.

Schauen wir uns das bestehende Backend mal an:
Code

 
<? 
include("./config.php"); 
$connect=@mysql_connect("$db_host""$db_user""$db_pass"
or die(
"<li>Cant connect to: $db_host  with user: $db_user</li>"); 
@
mysql_select_db($db$connect)or die("Cant select DB"); 
 
// hier wird jeder job aus der datenbank geholt, 
// der den staus planned hat und in der vergangenheit liegt. 
 
$sql="SELECT jid, job.rid, subject, description, email, reminder.interval, 
      reminder.remind_date, reminder.auth  
      FROM job, reminder  
      WHERE  
      status='planned' AND  
      planned<UNIX_TIMESTAMP() AND  
      reminder.rid=job.rid"

       
$result=mysql_query($sql); 
while(
$row=mysql_fetch_array($result)){ 
    
$header  'MIME-Version: 1.0' "n"
    
$header .= 'Content-type: text/html; charset=iso-8859-1' "n"
    
$header .= 'To: "'.$row['email'].'" <'.$row['email'].'>' "n"
    
$header .= 'From: "Remind ME" <remind@codejungle.org>' "n"
    
$msg=
    <html> 
    <head> 
      <title>RemindME</title> 
    </head> 
    <body> 
      '
.$row['description'].
          <br/><hr><a href="http://www.codejungle.org/reminder/uid/'
.$row[auth].'">Modify your Reminder</a> 
    </body> 
    </html> 
    '

    if(
mail($row['email'], $row['subject'], $msg$header)){ 
        
//wenn die email verschickt werden konnte, wird der job auf done gesetzt 
        //und ein neuer job mit neuem versand datum angelegt 
        
$update="update job set status='done' where jid=".$row['jid']; 
        
mysql_query($update); 
        
$time=getdate($row['remind_date']); 
        if(
$row['interval']=="d"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'], 
            
$time['mon'], $time['mday']+1$time['year']));     
        } 
        if(
$row['interval']=="w"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'], 
            
$time['mon'], $time['mday']+7$time['year']));     
        } 
        if(
$row['interval']=="m"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'],
            
$time['mon']+1$time['mday'], $time['year']));     
        } 
        if(
$row['interval']=="y"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'],
            
$time['mon'], $time['mday'], $time['year']+1));     
        } 
        if(
$row['interval']!=="n"){ 
            
$update_setting="update reminder set remind_date=".$dstr." where rid=".$row[rid]; 
            
mysql_query($update_setting);         
            
$insert="insert into job values ('', ".$row['rid'].", 'planned', ".$dstr.")"
            
mysql_query($insert); 
        } 
    } 

?> 


Im Grunde passiert in diesem Script folgendes:

Hole mir alle unerledigten Jobs
Versende eMail
Markiere Job als erledigt
Erstelle gegebenenfalls neuen Job

Dieser Teil zeigt ganz schön, was man beachten muss, wenn man eine Applikation umschreibt, damit
Sie auf mehreren Server laufen kann.

Da zwischen "Hole mir alle unerledigten Jobs" und "Markiere Job als erledigt" etwas Zeit vergehen kann,
kann es passieren das ein zweiter Server die gleiche Routine startet und
ebenfalls den gleichen Job zugeteilt bekommt. Das Resultat wäre, dass der Benutzer
zwei oder mehr gleiche Nachrichten erhält.

Dies werden wir umgehen, indem wir nach "Hole mir alle unerledigten Jobs" einen Task einbauen,
der dafür sorgt, dass andere Server nicht den gleichen Task bekommen.

Hole mir 10 unerledigte Jobs
Markiere Job als in Bearbeitung
Versende eMail
Markiere Job als erledigt
Erstelle gegebenenfalls neuen Job

Desweiteren werden wir die Anzahl der zu erledigenden Jobs pro Prozess auf 10 verringern.
Daher sollte unser Code nun folgendermaßen aussehen:
Code
 
<? 
include("./config.php"); 
$connect=@mysql_connect("$db_host""$db_user""$db_pass"
or die(
"<li>Cant connect to: $db_host  with user: $db_user</li>"); 
@
mysql_select_db($db$connect)or die("Cant select DB"); 
 
// hier werden die letzen 10 jobs aus der datenbank geholt, 
// der den staus planned hat und in der vergangenheit liegt. 
 
$sql="SELECT jid, job.rid, subject, description, email, reminder.interval,
      reminder.remind_date, reminder.auth  
      FROM job, reminder  
      WHERE  
      status='planned' AND  
      planned<UNIX_TIMESTAMP() AND  
      reminder.rid=job.rid  
      limit 10 
      "

       
$result=mysql_query($sql); 
while(
$row=mysql_fetch_array($result)){ 
    
//setze job auf in bearbeitung, damit kein anderer prozess ihn ebenfalls erledigt 
    
mysql_query("UPDATE job set staus='in_progress' where id='".$row['jid']."'"); 
    
$header  'MIME-Version: 1.0' "n"
    
$header .= 'Content-type: text/html; charset=iso-8859-1' "n"
    
$header .= 'To: "'.$row['email'].'" <'.$row['email'].'>' "n"
    
$header .= 'From: "Remind ME" <remind@codejungle.org>' "n"
    
$msg=
    <html> 
    <head> 
      <title>RemindME</title> 
    </head> 
    <body> 
      '
.$row['description'].
          <br/><hr><a href="http://www.codejungle.org/reminder/uid/'
.$row[auth].'">Modify your Reminder</a> 
    </body> 
    </html> 
    '

    if(
mail($row['email'], $row['subject'], $msg$header)){ 
        
//wenn die email verschickt werden konnte, wird der job auf done gesetzt 
        //und ein neuer job mit neuem versand datum angelegt 
        
$update="update job set status='done' where jid=".$row['jid']; 
        
mysql_query($update); 
        
$time=getdate($row['remind_date']); 
        if(
$row['interval']=="d"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'], 
            
$time['mon'], $time['mday']+1$time['year']));     
        } 
        if(
$row['interval']=="w"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'], 
            
$time['mon'], $time['mday']+7$time['year']));     
        } 
        if(
$row['interval']=="m"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'],
            
$time['mon']+1$time['mday'], $time['year']));     
        } 
        if(
$row['interval']=="y"){ 
            
$dstr=date("U"mktime($time['hours'], $time['minutes'], $time['seconds'],
            
$time['mon'], $time['mday'], $time['year']+1));     
        } 
        if(
$row['interval']!=="n"){ 
            
$update_setting="update reminder set remind_date=".$dstr." where rid=".$row[rid]; 
            
mysql_query($update_setting);         
            
$insert="insert into job values ('', ".$row['rid'].", 'planned', ".$dstr.")"
            
mysql_query($insert); 
        } 
    } 

?> 

Nachdem wir den Code nun soweit umgeschrieben haben, dass er auf mehren Servern laufen kann,
wäre eine Variante, ihn dort via Cron alle x Minuten auszuführen.

Im zweiten Teil möchte mit Hilfe dieses Beispiels, Gearman vorstellen, es ist ein Framework,
mit dem man parallele und lastenverteile Systeme aufbauen kann.

"It allows you to do work in parallel, to load balance processing, and to call functions between languages"

Außerdem möchte ich im dritten Teil noch etwas näher auf die Datenbankebene, memcache und verschiedene Lastenverteiler eingehen.

To be continuted

Andreas


Kommentare

TCaptcha - Einfache Einbindung von Captcha-Codes ueber PHP

Das ist der erste Co- Authoren Beitrag auf meiner Seite zum Thema Captchas.
Vielen Dank vorab an: Sereby (http://www.sereby.org/)


Man findet Sie überall und man kommt auch nicht mehr dran vorbei:
Captcha's (Completely Automated Public Turing test to tell Computers and Humans Apart)
Sie werden von Webseiten-Administratoren z.B. bei Kontaktformularen, Shoutboxen oder Downloads verwendet, um sicher zu stellen, dass Bots nicht die Möglichkeit haben die Shoutbox oder ähnliches voll zu spammen oder in einem Downloadbereich dafür zu sorgen, dass nicht unendlich viel Traffic verbraucht wird, weil der Downloadlink tausende Male ausgeführt wird.

Nun zum eigentlichen Code:
Download

zunächst muss die Klasse TCaptcha eingebunden werden um auf die Validierungsfunktionen zuzugreifen
Code


<?
include('captchaTCaptcha.php');
?>
<form action="" method="post" name="111">
    <!-- 
        Einbinden des Captcha's über ein <img> tag.
        Wenn mehrere Captcha's verwendet werden sollen, dann
        müssen Sie die ID verändern!
    -->
    <img src="captcha/show.php?id=1" alt="Sicherheitscode 1" />

    <input type="text" name="captcha_code1" size="13" /> <input type="submit" value="OK" />

    <?
        
//code überprüfen
        
if (isset($_POST['captcha_code1'])){
            
// Validate(* Getippter Code *, * Captcha-ID die verglichen werden soll *) 
            
if (TCaptcha::Validate($_POST['captcha_code1'], 1)){
                echo 
'Code OK!';
            }else{
                echo 
'Falscher Code eingegeben!';
            }
        }
    
?>
</form>


Nun die eigentliche Captcha Klasse

Code

<?php
session_start
();
define('CAPTCHA_SESSION_ID''php_captcha');
define('CAPTCHA_IGNORE_CASE'false);

class 
TCaptcha
{
    var 
$IgnoreCase;
    var 
$CharSet;
    var 
$font;
    var 
$font_size;
    var 
$width;
    var 
$height;
    var 
$bgimg;
    var 
$maxlen;

    public 
$Code;

    public function 
__construct($FontFile './xfiles.ttf'$iWidth 100$iHeight 40)
    {
        
$this->IgnoreCase CAPTCHA_IGNORE_CASE;
        
$this->font       $FontFile;
        
$this->bgimg      './bg.png';
        
$this->maxlen     5;
        
$this->font_size  20;
        
$this->CharSet    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        
$this->SetWidth($iWidth);
        
$this->SetHeight($iHeight);
        
$this->Code '';
    }
    
    function 
SetWidth($iWidth)
    {
        
$this->width $iWidth;
        if (
$iWidth 500$this->width 500;
    }

    function 
SetHeight($iHeight)
    {
        
$this->height $iHeight;
        if (
$iHeight 200$this->height 200;
    }
    
    private function 
randomString($len)
    {
        
//zufallszahl
        
function make_seed()
        {
            list(
$usec $sec) = explode (' 'microtime());
            return (float) 
$sec + ((float) $usec 100000);
        }
        
srand(make_seed());

        
//zufallsstring
        
$str '';
        while (
strlen($str)<$len)
        {
            
$str .= substr($this->CharSet, (rand()%(strlen($this->CharSet))), 1);
        }
           return(
$str);
    }
   
    function 
GenerateCode()
    {
        
//captcha-id
        
$CaptchaID = (is_numeric($_GET['id']) ? $_GET['id'] : '1');
        
        
//code generieren
        
$this->Code $this->randomString($this->maxlen);
        
        
//groß / kleinschreibung beachten?
        
if ($this->IgnoreCase)
        {
            
$this->Code strtoupper($this->Code);
        } 

        
//in die Session schreiben
        
$_SESSION[CAPTCHA_SESSION_ID][$CaptchaID] = $this->Code;
    }

    function 
Create()
    {
        
//Hintergrundbild einlesen
        
$picinfos GetImageSize($this->bgimg);
        
$OldIMG   ImageCreateFromPNG($this->bgimg);
        
        
//größe anpassen
        
$img      ImageCreateTrueColor($this->width$this->height);
        
ImageCopyResized($img,$OldIMG,0,0,0,0,$this->width,$this->height,$picinfos[0],$picinfos[1]);

        
//Farbe der Schrift
        
$color ImageColorAllocate($img000); 
        
        
//ZufallsCode generieren
        
$this->GenerateCode();
        
        
//justierung
        
$spacing = ($this->width $this->maxlen);
        
$t_y_min min($this->height$this->font_size);
        
$t_y   rand($t_y_min 1.2$t_y_min 2.8) + $spacing;
        
$angle rand(0,7);
        
$t_x   rand(5,10);
        
$a     0;

        
//text auf das bild schreiben. Zeichen für Zeichen
        
for ($i 1$i <=$this->maxlen$i++)
        {
            
ImageTTFText($img$this->font_size$angle$t_x$t_y$color$this->font$this->Code{$a});
            
$t_x $t_x $spacing -3;
            
$a++; // nächstes zeichen
        
}
        
        
//bild anzeigen
        
header("Content-type: image/gif");
        
ImageGIF($img);
        
        
//bild freigeben
        
ImageDestroy($img);
    }
    
    function 
Validate($MyCode$ID 1$IgnoreCase CAPTCHA_IGNORE_CASE)
    {
        if (
$IgnoreCase)
        {
            
$MyCode strtoupper($MyCode);
        }

        if (!empty(
$_SESSION[CAPTCHA_SESSION_ID][$ID]) && $MyCode == $_SESSION[CAPTCHA_SESSION_ID][$ID])
        {
            unset(
$_SESSION[CAPTCHA_SESSION_ID][$ID]); //wiederbenutzen vermeiden
            
return true;
        }
        return 
false;
    }
}
?>


Fertig!

Nachtrag von Andreas:

Es gibt auch Möglichkeiten ein Captcha zu umgehen, dies sei an dieser Stelle noch erwähnt. In der Praxis sind die Methoden allerdings eher ineffektiv oder doch etwas kompliziert.

Trotzdem möchte ich kurz auf ein, zwei Möglichkeiten hinweißen.

OCR Software, also Texterkennungssoftware die das Captcha versucht zu interpretieren. Ist in
der Anwendung kompliziert und schwer zu implementieren, aber dennoch der erfolgsversprechendste Ansatz.

Ich habe auch mal von der Möglichkeit gehört, die Captcha bei seiner privaten Seite einzubinden
um seine Besucher die Arbeit erledigen zu lassen indem man sie dazu nötigt die Captchas zu entschlüsseln.
Dies gelingt natürlich nur auf einer interessanten Seite mit viel Traffic.

Andere Varianten nutzen meist nur schlecht programmierte Captcha aus, bei denen z.b. das Lösungswort
in einem hidden field steht...

Vielen Dank Sereby für deinen ersten Beitrag!

Andreas


Kommentare

Seiten:1 - 2 - 3