PHP für Fortgeschrittene

610644ae9328059c33a272d08b90c75d?s=47 Oliver Klee
September 06, 2018

PHP für Fortgeschrittene

610644ae9328059c33a272d08b90c75d?s=128

Oliver Klee

September 06, 2018
Tweet

Transcript

  1. September 2018 Oliver Klee | @oliklee
 typo3-coding@oliverklee.de PHP für Fortgeschrittene

  2. Paket- Namen

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

    / symfony php ext-zip
  4. Autoloading mit PSR-4

  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
  6. Sauberer Code

  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); }
  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;
  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)) {
  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; }
  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;
  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)) {
  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; }
  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; }
  15. Codemetrik:
 zyklomatische Komplexität „Anzahl der binären Verzweigungen plus eins (für

    die Kanne).“ auch:
 Mc-Cabe-Metrik
  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
  17. Codemetrik:
 Npath-Komplexität „Anzahl der (azyklischen) Ausführungspfade.“ Mindest-Anzahl nötiger Unit-Tests

  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
  19. Codemetrik:
 Npath-Komplexität public static function writeFile($file, $content, $changePermissions = false)

    { if (!@is_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
  20. Codemetriken:
 Kopplung und Kohäsion Klasse Klasse Klasse Kopplung Kohäsion

  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
  22. Software- Architektur

  23. Domain-driven-Design ubiquitous language

  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.“
  25. Open-Closed-Prinzip „Modules should be both open (for extension) and closed

    (for modification).”
  26. Single-Responsibility- Prinzip „Eine Klasse oder Methode sollte genau eine Sache

    tun (und das gut).”
  27. Themen- Teaser

  28. Design-Patterns

  29. Design-Patterns

  30. Test-driven-Development write
 test write
 code refactor

  31. Security in Webanwendungen

  32. Domain-driven-Design

  33. Git und Git-Workflows

  34. PhpStorm effizient nutzen

  35. Debugging und Profiling
 mit Xdebug und XHprof