PHP encode String zu Extended ASCII 8bit 255 Zeichen

Um auch Umlaute in ein Bit darzustellen, kann man den erweiterten ASCII Zeichensatz verwenden mit 255 Zeichen, der auch Umlaute enthält wie ä, ö, ü und ß.

Um dieses zu nutzen, kann man die folgende Funktion nutzen:

/**
 * @param string $string
 * @return string
 */
protected function asciiEncodeString(string $string): string
{
    $sourceEncoding = mb_detect_encoding($string);
    $destinationEncoding = 'CP437'; // Extended ASCII - Codepage 437
    $string = iconv($sourceEncoding, $destinationEncoding, $string);
    return $string;
}

UDP mit PHP Nachrichten senden und empfangen Beispiel

Mit PHP kann man über UDP auf dem eigenen Rechner sehr einfach Packete verschicken. Um dies zu testen, braucht man einen Sender und einen Empfänger. In dem Biepsiel kommunizieren beider über die lokale IP auf Port 20010. Die Beispiele müssen auf der Konsole ausgeführt werden.

Sender:

<?php
$address = '127.0.0.1';
$port = 20010;
$beat_period = 1;

$fp = stream_socket_client("udp://$address:$port", $errno, $errstr);
if (!$fp) {
 die("ERROR: $errno - $errstr");
}

while (true) {
 $message = sprintf(
 '%s send: %s'. PHP_EOL,
 date('c'),
 rand(0, 1000000)
 );
 fwrite($fp, $message);
 echo $message;

 sleep($beat_period);
}

Empfänger:

<?php

$address = '127.0.0.1';
$port = 20010;

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, $address, $port);

if ($socket === false) {
 throw new \RuntimeException(
 sprintf(
 'could not connect to socket address %s on port %s. Error: %s %s',
 $address,
 $port,
 socket_last_error(),
 socket_strerror(socket_last_error())
 )
 );
}

while (true){
 echo socket_read ($socket, 1024);
}

mehrer UDP Frames mit PHP parallel auslesen mit socket_select()

Man kann in PHP sehr schwer parallel Operationen ausführen, aber für das lesen von mehreren Sockets gibt es die socket_select() Funktion. Damit lassen sich mehrere Socket Verbindungen parallel auslesen.

In dem Beispiel werden 2 UDP Socket Verbindungen erstellt und gleichzeitig ausgelesen:

function createSocket(string $ip, int $port)
{
    $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    //set non blocking read
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
    socket_bind($socket, $ip, $port);

    if ($socket === false) {
        throw new \RuntimeException(
            sprintf(
                'could not connect to socket address %s on port %s. Error: %s %s',
                $ip,
                $port,
                socket_last_error(),
                socket_strerror(socket_last_error())
                )
        );
    }
    return $socket;
}
function readSockets()
{
 $waitTimeoutSeconds = 1;

 $socket1 = $this->createSocket('127.0.0.1', 20001);
 $socket2 = $this->createSocket('127.0.0.1', 20002);

 $sockets['socket1'] = $socket1;
 $sockets['socket2'] = $socket2;

 $read = $sockets;
 $write = null;
 $except = null;

 if (socket_select($read, $write , $except, $waitTimeoutSeconds))
 {
 // loop through the sockets that showed activity
 if (isset($read['socket1'])) {
    // socket 1 got a message
    $content1 = socket_read ($socket1, 1024);
 }
 if (isset($read['socket2'])) {
    // socket 2 got a message
    $content2 = socket_read ($socket2, 1024);
 }

 } else {
 throw new \RuntimeException('could not read any socket');
 }

 socket_close($socket1);
 socket_close($socket2);
}

PHP XMLReader für sehr große Dateien Beispiel

Um mit PHP große XML Dateien auswerten zu können, muss man einen SAX Parser verwenden, der die XML Dateien von oben nach unten durchliest und nicht in ein Objekt umwandelt. Dafür ist der XMLReader von PHP vorgesehen.

Ein Beispiel:

$data = new Data();
$reader = new \XMLReader();
$reader->open($file);

while ($reader->read()) {
    if ($reader->nodeType == \XMLReader::ELEMENT) {
        switch ($reader->name) {
            case "tagName1" :
                $node = new \SimpleXMLElement($reader->readOuterXML());
                $attributes = $node->attributes();
                $entity = new Entity();
                $entity->setId($attributes['id']);
                $entity->setName($attributes['name']);
                $entity->setCode($attributes['code']);
                $data->addEntity($entity);

                break;

            case  "tagName2":
                $node = new \SimpleXMLElement($reader->readOuterXML());
                $attributes = $node->attributes();
                $entity = new OtherEntity();
                $entity->setId($attributes['id']);
                $entity->setName($attributes['name']);
                $entity->setCode($attributes['code']);
                $data->addOtherEntity($entity);

                break;
        }
    }
}

Twig Extension zum Sortieren von Entitäten per Datetime Property

Wenn man im Template eine Doctrine Collection sortieren will nach einem Zeitstempel (createdAt in dem Beispiel), sollte man dies eigentlich vorher machen.

Wenn dies nicht möglich ist, z.B. im Sonata Admin Bundle, dann kann man diese Twig Extension verwenden:

{% foo| sortByCreatedAt('asc') %}

Twig Extension Code:

<?php

namespace App\Twig;

use App\Entity\Tag;
use Doctrine\ORM\PersistentCollection;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class AppExtension extends AbstractExtension
{
    public function getFilters()
    {
        return array(
            new TwigFilter('sortByCreatedAt', array($this, 'sortByCreatedAt')),
        );
    }

    /**
     * @param PersistentCollection $objects
     * @return mixed
     */
    public function sortByCreatedAt($objects, $direction = 'asc')
    {
        $objects = $objects->toArray();
        usort($objects, function ($a, $b) use($direction) {
            if ($direction === 'asc') {
                return $a->getCreatedAt() >  $b->getCreatedAt();
            } elseif ($direction === 'desc') {
                return $a->getCreatedAt() <  $b->getCreatedAt();
            } else {
                throw new \Exception('unknown sort direction');
            }

        });
        return $objects;
    }
}

Strato und MySQL: General error: 1709 Index column size too large. The maximum column size is 767 bytes.

Bei einem Kunden wurde mir folgende Fehlermeldung angezeigt, wenn ich versucht habe über Symfony die Datenbank erstellen zu lassen:

General error: 1709 Index column size too large. The maximum column size is 767 bytes.

Dies liegt daran, das Strato so komische Einstellung bei ihren Managed Hosting Packete wie z.B. das STRATO PowerWeb hat. Strato wird diese Einstellung leider nicht ändern, aber man kann in Symfony in der doctrine.yaml (config.yaml) das Charset ändern, dann funktioniert Symfony auch auf einem Strato Server:

doctrine:
    dbal:
        # configure these for your database server
        driver: 'pdo_mysql'
        server_version: '5.6'
        charset: utf8
        default_table_options:
            charset: utf8
            collate: utf8_general_ci

Symfony Security Passwörter hashen mit dem PasswordEncoder

Der PasswordEncoder des Symfony Frameworks ist sehr gut geeignet auch in Zukunft sichere Hashes von Passwörtern in der Datenbank zu speichern und zentral zu konfigurieren.

Das SecurityBundle muss ggf. nachinstalliert werden:

composer require symfony/security-bundle

Man legt dazu in der security.yaml fest, welchen Hashing Algorithmus man verwenden will für welche Entität:

security:
    encoders:
        App\Entity\User: bcrypt

Die Entität muss das UserInterface implementieren: weiterlesen…

Zertifizierung zum Zend Certified Engineer erfolgreich bestanden

Am 1. März habe ich die Prüfung erfolgreich abgelegt für die ich die letzten Monate gelernt habe. Wie zu erwarten war, waren die Fragen sehr, sehr kniffelig, aber die Vorbereitung hat sich bezahlt gemacht.

zce-2017-php-80x80

Vorbereitung

Ich kann jedem empfehlen sich vorher merhmals das Buch PHP7 Zend Certification Study Guide durchzulesen:

Außerdem sollte man alle Tests auf der Seite zendexam.com zu 80% erfolgreich beantworten können, dann ist man bereit für die Prüfung.

Anmeldung für die Prüfung

Man kann sich auf der zend.com Seite anmelden für die Prüfung , die man dann in der Nähe in einem Testcenter seiner Wahl absolvieren kann unter strengsten Bedingungen.

Einmal im Jahr gibt es einen Gutschein für die Prüfung auf retailmenot.com, wenn man Glück hat.

Travis CI: No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)

Bei der sehr kryptischen Fehlermeldung in Travis CI:

No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)

handlet es sich um ein Problem mit falschen Einrückungen im travis.yml File:

matrix:
  include:
    - php: 5.6
      env:
        - SYMFONY_VERSION='3.4.*'

Ein Leerzeichen zu wenig in der Zeile – SYMFONY_VERSION=’3.4.*’ und es kommt zu diesem wenig aufschlussreichen Fehler.