Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP für Fortgeschrittene

Oliver Klee
September 06, 2018

PHP für Fortgeschrittene

Oliver Klee

September 06, 2018
Tweet

More Decks by Oliver Klee

Other Decks in Technology

Transcript

  1. September 2018
    Oliver Klee | @oliklee

    [email protected]
    PHP für Fortgeschrittene

    View Slide

  2. Paket-
    Namen

    View Slide

  3. Vendor / Product
    pelago / emogrifier
    typo3 / cms-core
    symfony / symfony
    php
    ext-zip

    View Slide

  4. Autoloading
    mit PSR-4

    View Slide

  5. composer.json
    "autoload": {
    "psr-4": {
    "OliverKlee\\GeosShop\\": "Classes/"
    }
    },
    „autoload-dev“: {
    "psr-4": {
    „OliverKlee\\GeosShop\\Tests\\“: „Tests/"
    }
    }
    \OliverKlee\GeosShop\Domain\Model\Product
    Classes/Domain/Model/Product.php
    \OliverKlee\GeosShop\Tests\Unit\Domain\Model\ProductTest
    Tests/Unit/Domain/Model/ProductTest.php

    View Slide

  6. Sauberer
    Code

    View Slide

  7. Antipattern: Early Returns
    public static function cmpIP($baseIP, $list)
    {
    $list = trim($list);
    if ($list === '') {
    return false;
    }
    if ($list === '*') {
    return true;
    }
    if (strpos($baseIP, ':') !== false && self::validIPv6($baseIP)) {
    return self::cmpIPv6($baseIP, $list);
    }
    return self::cmpIPv4($baseIP, $list);
    }

    View Slide

  8. public static function getUrl($url, $includeHeader = 0, $requestHeaders = null, &$report =
    null)
    {
    if (isset($report)) {
    $report['error'] = 0;
    $report['message'] = '';
    }
    // Looks like it's an external file, use Guzzle by default
    if (preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
    /** @var RequestFactory $requestFactory */
    $requestFactory = static::makeInstance(RequestFactory::class);
    if (is_array($requestHeaders)) {
    // Check is $requestHeaders is an associative array or not
    if (count(array_filter(array_keys($requestHeaders), 'is_string')) === 0) {
    trigger_error('Request headers as colon-separated string are deprecated, use
    an associative array instead.', E_USER_DEPRECATED);
    // Convert cURL style lines of headers to Guzzle key/value(s) pairs.
    $requestHeaders = static::splitHeaderLines($requestHeaders);
    }
    $configuration = ['headers' => $requestHeaders];
    } else {
    $configuration = [];
    }
    $includeHeader = (int)$includeHeader;
    $method = $includeHeader === 2 ? 'HEAD' : 'GET';
    try {
    if (isset($report)) {
    $report['lib'] = 'GuzzleHttp';
    }
    $response = $requestFactory->request($url, $method, $configuration);
    } catch (RequestException $exception) {
    if (isset($report)) {
    $report['error'] = $exception->getCode() ?: 1518707554;
    $report['message'] = $exception->getMessage();
    $report['exception'] = $exception;

    View Slide

  9. } catch (RequestException $exception) {
    if (isset($report)) {
    $report['error'] = $exception->getCode() ?: 1518707554;
    $report['message'] = $exception->getMessage();
    $report['exception'] = $exception;
    }
    return false;
    }
    $content = '';
    // Add the headers to the output
    if ($includeHeader) {
    $parsedURL = parse_url($url);
    $content = $method . ' ' . ($parsedURL['path'] ?? '/')
    . (!empty($parsedURL['query']) ? '?' . $parsedURL['query'] : '') . ' HTTP/
    1.0' . CRLF
    . 'Host: ' . $parsedURL['host'] . CRLF
    . 'Connection: close' . CRLF;
    if (is_array($requestHeaders)) {
    $content .= implode(CRLF, $requestHeaders) . CRLF;
    }
    foreach ($response->getHeaders() as $headerName => $headerValues) {
    $content .= $headerName . ': ' . implode(', ', $headerValues) . CRLF;
    }
    // Headers are separated from the body with two CRLFs
    $content .= CRLF;
    }
    $content .= $response->getBody()->getContents();
    if (isset($report)) {
    if ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
    $report['http_code'] = $response->getStatusCode();
    $report['content_type'] = $response->getHeaderLine('Content-Type');
    $report['error'] = $response->getStatusCode();
    $report['message'] = $response->getReasonPhrase();
    } elseif (empty($content)) {

    View Slide

  10. if (isset($report)) {
    if ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
    $report['http_code'] = $response->getStatusCode();
    $report['content_type'] = $response->getHeaderLine('Content-Type');
    $report['error'] = $response->getStatusCode();
    $report['message'] = $response->getReasonPhrase();
    } elseif (empty($content)) {
    $report['error'] = $response->getStatusCode();
    $report['message'] = $response->getReasonPhrase();
    } elseif ($includeHeader) {
    // Set only for $includeHeader to work exactly like PHP variant
    $report['http_code'] = $response->getStatusCode();
    $report['content_type'] = $response->getHeaderLine('Content-Type');
    }
    }
    } else {
    if (isset($report)) {
    $report['lib'] = 'file';
    }
    $content = @file_get_contents($url);
    if ($content === false && isset($report)) {
    $report['error'] = -1;
    $report['message'] = 'Couldn\'t get URL: ' . $url;
    }
    }
    return $content;
    }

    View Slide

  11. public static function getUrl($url, $includeHeader = 0, $requestHeaders = null, &$report =
    null)
    {
    if (isset($report)) {
    $report['error'] = 0;
    $report['message'] = '';
    }
    // Looks like it's an external file, use Guzzle by default
    if (preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
    /** @var RequestFactory $requestFactory */
    $requestFactory = static::makeInstance(RequestFactory::class);
    if (is_array($requestHeaders)) {
    // Check is $requestHeaders is an associative array or not
    if (count(array_filter(array_keys($requestHeaders), 'is_string')) === 0) {
    trigger_error('Request headers as colon-separated string are deprecated, use
    an associative array instead.', E_USER_DEPRECATED);
    // Convert cURL style lines of headers to Guzzle key/value(s) pairs.
    $requestHeaders = static::splitHeaderLines($requestHeaders);
    }
    $configuration = ['headers' => $requestHeaders];
    } else {
    $configuration = [];
    }
    $includeHeader = (int)$includeHeader;
    $method = $includeHeader === 2 ? 'HEAD' : 'GET';
    try {
    if (isset($report)) {
    $report['lib'] = 'GuzzleHttp';
    }
    $response = $requestFactory->request($url, $method, $configuration);
    } catch (RequestException $exception) {
    if (isset($report)) {
    $report['error'] = $exception->getCode() ?: 1518707554;
    $report['message'] = $exception->getMessage();
    $report['exception'] = $exception;

    View Slide

  12. } catch (RequestException $exception) {
    if (isset($report)) {
    $report['error'] = $exception->getCode() ?: 1518707554;
    $report['message'] = $exception->getMessage();
    $report['exception'] = $exception;
    }
    return false;
    }
    $content = '';
    // Add the headers to the output
    if ($includeHeader) {
    $parsedURL = parse_url($url);
    $content = $method . ' ' . ($parsedURL['path'] ?? '/')
    . (!empty($parsedURL['query']) ? '?' . $parsedURL['query'] : '') . ' HTTP/
    1.0' . CRLF
    . 'Host: ' . $parsedURL['host'] . CRLF
    . 'Connection: close' . CRLF;
    if (is_array($requestHeaders)) {
    $content .= implode(CRLF, $requestHeaders) . CRLF;
    }
    foreach ($response->getHeaders() as $headerName => $headerValues) {
    $content .= $headerName . ': ' . implode(', ', $headerValues) . CRLF;
    }
    // Headers are separated from the body with two CRLFs
    $content .= CRLF;
    }
    $content .= $response->getBody()->getContents();
    if (isset($report)) {
    if ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
    $report['http_code'] = $response->getStatusCode();
    $report['content_type'] = $response->getHeaderLine('Content-Type');
    $report['error'] = $response->getStatusCode();
    $report['message'] = $response->getReasonPhrase();
    } elseif (empty($content)) {

    View Slide

  13. if (isset($report)) {
    if ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
    $report['http_code'] = $response->getStatusCode();
    $report['content_type'] = $response->getHeaderLine('Content-Type');
    $report['error'] = $response->getStatusCode();
    $report['message'] = $response->getReasonPhrase();
    } elseif (empty($content)) {
    $report['error'] = $response->getStatusCode();
    $report['message'] = $response->getReasonPhrase();
    } elseif ($includeHeader) {
    // Set only for $includeHeader to work exactly like PHP variant
    $report['http_code'] = $response->getStatusCode();
    $report['content_type'] = $response->getHeaderLine('Content-Type');
    }
    }
    } else {
    if (isset($report)) {
    $report['lib'] = 'file';
    }
    $content = @file_get_contents($url);
    if ($content === false && isset($report)) {
    $report['error'] = -1;
    $report['message'] = 'Couldn\'t get URL: ' . $url;
    }
    }
    return $content;
    }

    View Slide

  14. Pattern: Guard-Clauses
    public static function fixed_lgd_cs($string, $chars, $appendString = '...')
    {
    if ((int)$chars === 0 || mb_strlen($string, 'utf-8') <= abs($chars)) {
    return $string;
    }
    if ($chars > 0) {
    $string = mb_substr($string, 0, $chars, 'utf-8') . $appendString;
    } else {
    $string = $appendString . mb_substr($string, $chars, mb_strlen($string,
    'utf-8'), 'utf-8');
    }
    return $string;
    }

    View Slide

  15. Codemetrik:

    zyklomatische Komplexität
    „Anzahl der binären
    Verzweigungen plus eins
    (für die Kanne).“
    auch:

    Mc-Cabe-Metrik

    View Slide

  16. Codemetrik:

    zyklomatische Komplexität
    public static function fixed_lgd_cs($string, $chars, $appendString = '...')
    {
    if ((int)$chars === 0 || mb_strlen($string, 'utf-8') <= abs($chars)) {
    return $string;
    }
    if ($chars > 0) {
    $string = mb_substr($string, 0, $chars, 'utf-8') . $appendString;
    } else {
    $string = $appendString . mb_substr($string, $chars, mb_strlen($string,
    'utf-8'), 'utf-8');
    }
    return $string;
    }
    1
    2
    3

    View Slide

  17. Codemetrik:

    Npath-Komplexität
    „Anzahl der
    (azyklischen)
    Ausführungspfade.“
    Mindest-Anzahl
    nötiger Unit-Tests

    View Slide

  18. Codemetrik:

    Npath-Komplexität
    public static function fixed_lgd_cs($string, $chars, $appendString = '...')
    {
    if ((int)$chars === 0 || mb_strlen($string, 'utf-8') <= abs($chars)) {
    return $string;
    }
    if ($chars > 0) {
    $string = mb_substr($string, 0, $chars, 'utf-8') . $appendString;
    } else {
    $string = $appendString . mb_substr($string, $chars, mb_strlen($string,
    'utf-8'), 'utf-8');
    }
    return $string;
    }
    1
    2
    3

    View Slide

  19. Codemetrik:

    Npath-Komplexität
    public static function writeFile($file, $content, $changePermissions = false)
    {
    if ([email protected]_file($file)) {
    $changePermissions = true;
    }
    if ($fd = fopen($file, 'wb')) {
    $res = fwrite($fd, $content);
    fclose($fd);
    if ($res === false) {
    return false;
    }
    // Change the permissions only if the file has just been created
    if ($changePermissions) {
    static::fixPermissions($file);
    }
    return true;
    }
    return false;
    }
    1
    2
    4
    6
    8

    View Slide

  20. Codemetriken:

    Kopplung und Kohäsion
    Klasse
    Klasse
    Klasse
    Kopplung
    Kohäsion

    View Slide

  21. Design-by-Contract (light)
    /**
    * Generate code from tokens between given indexes.
    *
    * @param int $start start index, must be >= 0
    * @param int $end end index, must be > $start
    *
    * @return string code, will not be empty
    */
    public function generatePartialCode(int $start, int $end)
    Nachbedingungen
    Vorbedingungen

    View Slide

  22. Software-
    Architektur

    View Slide

  23. Domain-driven-Design
    ubiquitous
    language

    View Slide

  24. Liskovsches
    Substitutionsprinzip
    (Ersetzbarkeitsprinzip)
    „Wo eine Instanz einer Klasse benutzt wird,
    muss auch eine Instanz einer Unterklasse
    benutzt werden können, ohne dass dadurch
    Fehler auftreten.“

    View Slide

  25. Open-Closed-Prinzip
    „Modules should be both open (for extension)
    and closed (for modification).”

    View Slide

  26. Single-Responsibility-
    Prinzip
    „Eine Klasse oder Methode sollte genau eine
    Sache tun (und das gut).”

    View Slide

  27. Themen-
    Teaser

    View Slide

  28. Design-Patterns

    View Slide

  29. Design-Patterns

    View Slide

  30. Test-driven-Development
    write

    test
    write

    code
    refactor

    View Slide

  31. Security in Webanwendungen

    View Slide

  32. Domain-driven-Design

    View Slide

  33. Git und Git-Workflows

    View Slide

  34. PhpStorm effizient nutzen

    View Slide

  35. Debugging und Profiling

    mit Xdebug und XHprof

    View Slide