PSR-11: Container interface

This document describes a common interface for dependency injection containers – тоест, когато имаме клас(ове), които ще използваме като data containers, той/те трябва да имплементират Psr\Container\ContainerInterface и задължително да имат поне метод get() и has().

The goal set by ContainerInterface is to standardize how frameworks and libraries make use of a container to obtain objects and parameters (called entries in the rest of this document).

The Psr\Container\ContainerInterface exposes two methods: get() and has()

Exceptions directly thrown by the container SHOULD implement the Psr\Container\ContainerExceptionInterface.

A call to the get method with a non-existing id MUST throw a Psr\Container\NotFoundExceptionInterface

Всеки елемент, съдържащ се в даденият контейнер, трябва да има уникален „oppaque string“, който уникално да го индентифицира. По този opaque string можем да изпоплзваме get(string) за да извлечем даденият елемент, или has(string) за да проверим дали такъв елемент съществува.

Специално за get() дали винаги по дадедн ID да връща един и същ резултат или не. Няма категоричност. Може и така и така.
Two successive calls to get with the same identifier SHOULD return the same value. However, depending on the implementor design and/or user configuration, different values might be returned, so user SHOULD NOT rely on getting the same value on 2 successive calls.

Литератра:

https://www.php-fig.org/psr/psr-11/

PSR-7: HTTP message interfaces

Относно методите, със get… вземаш, със with… променяш но първо клонираш.

Всичко в WWW се базира на клиент-сървър архитектурата и съответно – размяната на HTTP messages между тях – клиентът и сървът.

Относно натрапчивият въпрос, ОК какво разбираме под Request, или по-скоро – от коя страна да го гледаме? Tова, което клиентът изпраща, още от негова страна? Или това, което сървът е приел от негова страна?

Съдейки по този цитат:
Web browsers and HTTP clients such as cURL create HTTP request messages that are sent to a web server, which provides an HTTP response message.
Излиза, че е едно и също, и Рекуестът, и Респонсът, независимо от коя страна го гледаме. Нали все пак това, което изпраща клиентът е това, което получава сървът? Какво друго да е?

Прехвърлянето не променя нищо.

Рекуестът е рекуест и за рекуестващият, и за рекуестнатият, един и същ е, като волейболна топка.

Репонсът е респонс и за респондващият, и за респонднатият, пак на принципа на волейболната топка, едно и също е и за двете страни.

Демек, това което изпраща клиентът и съответно получава сървът, и за двете страни е един и същ Request.

И съответно и за Respons-ът.

Каквото подадеш е рекуест и за двете страни, каквото отговориш е респонс и за двете страни.

Но ако гледаме на дадените рекуести и респонси само от гледна точка на HTTP протоколът.

RequestInterface provides the general representation of an HTTP request message. However, server-side requests need additional treatment, due to the nature of the server-side environment.

Демек, когато сървът получи HTTP рекуест (RequestInterface), той от своя страна създава нов обект от ServerRequestInterface според своите си настройки и environment…
Това вече е ServerRequestInterface рекуест обект.

Демек, едното е така да се каже чистият HTTP рекуест, другото – нещо като разширената му версия според сърва, за да може да работим с него, с HTTP рекуеста. В какъв смисъл „разширената му версия“? В смисъл такъв, че например данните от HTTP рекуеста са парсирани и така да се каже, разпределени по масиви като $_GET, $_POST, $_COOKIE…

Messages Immutability

Messages are considered immutable; all methods that might change state MUST be implemented such that they retain the internal state of the current message and return an instance that contains the changed state.
Демек, нищо не променяш от това, което са ти подали, пазиш го непромененo. От него клонираш нов обект, в него променяш, и него използваш. Оригиналният запазваш напокътнат. На клонираният може всичко да променяш, но оригиналният трябва да си остане напокътнат.

PSR-7 препоръчва как едно PHP приложение, чисто откъм програмна страна, да приема HTTP заявките от клиента, и да създава HTTP отговорите, като задава интерфейси, които задават цялостна структура на класовете и методите, вършещи това.

This specification defines interfaces for the HTTP messages (request and response)  Psr\Http\Message\RequestInterface and Psr\Http\Message\ResponseInterface  respectively. Това сa двата интерфейса, които класовете за приемане на HTTP заявките и изпращане на HTTP отговорите, трябва да имплементират.

И двата наследяват Psr\Http\Message\MessageInterface

Но една такава заявка (Request) съдържа и много информация, извън самият HTTP протокол, информация свързана със самият сървър, демек това, което в PHP е в суперглобалният масив $_SERVER.
Затова трябва да имаме интерфейс Psr\Http\Message\ServerRequestInterface, наследяващ RequestInterface и работещ с информация от HTTP заявката, като:

  • The values represented in $_SERVER
  • Any cookies provided (generally via $_COOKIE)
  • Query string arguments (generally via $_GET, or as parsed via parse_str())
  • Upload files, if any (as represented by $_FILES)
  • Deserialized body parameters (generally from $_POST)
Concerning headers

Headers are retrieved by name from classes implementing the MessageInterface in a case-insensitive manner. For example, retrieving the foo header will return the same result as retrieving the FoO header. Similarly, setting the Foo header will overwrite any previously set foo header value.

Методите за извличане и сетване на хедъри(те) на дадено HTTP съобщение се задават в Psr\Http\Message\MessageInterface

Интересно е, че при извличане/променяне на хедър (което става по име), името му е case insensitive, демек няма разлика между FoO и fOO що се отнася до името на даденият хедър.

Streams

Не винаги е безопасно цялото боди на рекуеста да се запазва в променлива, защото може да бъде огромен и това да претовари сърва като му изчерпи паметта. Или просто да надвиши настройки на PHP като upload_max_filesize или post_max_size.

Without streams, opening a 20MB file will consume 20MB of memory.

Също, относно immutability. Няма такава за streams.
Защо?
Защото за да работиш със стрийм по принцип, трябва да можеш например динамично да задаваш поинтъра, където си в момента…

Стриймовете не са като обикновеният рекуест, който го получаваш an block, на цяло, на веднъж. Те са нещо динамично, пристигащо на части, така да се каже. Няма как от началото още да знаеш какво ще съдържа като цяло.

…immutability is impossible to enforce, as any code that interacts with the resource can potentially change its state (including cursor position, contents, and more)...
Демек, самото взаимодействие със стрийма може да го промени.

Достъпът до стриймовете могат да са read only, write only, randomly accessed

Server request

Сами по себе си RequestInterface обектите съдържат информация само и чисто за самият HTTP рекуест. Освен RequestInterface обект, имаме и ServerRequestInterface обект, който добавя към гореспоменатият обект още информация, свързана с конкретният сърв.

Литература:

https://www.php-fig.org/psr/psr-7/

https://www.php-fig.org/psr/psr-7/meta/#71-validation-of-header-names-and-values

PSR-4: Autoloader

Този PSR задава препоръки за файлструктурата на приложението, имената на клас-файловете и на самите класове, интерфейси и трейтове, с оглед на това да може стандартно да се аутолоудват.

  1. Трябва стандартът да е
    \<NamespaceName>\*\<ClassName>
  2. Tрябва за започва с vendor name – The fully qualified class name MUST have a top-level namespace name, also known as a „vendor namespace“.
  3. The fully qualified class name MAY have one or more sub-namespace names.
  4. Tрябва да завършва с името на класа/интерфейса/трейта (т.н. terminating class name)
  5. Подчертавки може да има но те нямат никакво смислово значение

Литература:

https://www.php-fig.org/psr/psr-4/

PSR-3: Logger Interface

The word implementor in this document is to be interpreted as someone implementing the LoggerInterface

Става дума за това какъв интерфейс трябва да имат logging библиотеките на едно PHP приложение. Kоито библиотеки трябва да получават обект от интерфейс Psr\Log\LoggerInterface, който разбира се може да бъде имплементиран според конкретните нужди.

LoggerInterface интерфейсът предлага 8 метода за запазване на лог информацията според нивата на грешките – debug, info, notice, warning, error, critical, alert, emergency (според rfc5424).

Има и девети метод log, който приема като първи аргумент – някое от горните нива като стринг, и извикването му трябва да дава същият резултат като съответният от горните методи. Подаването на аргумент, който не е от някой от горние нива, трябва да хвърля Psr\Log\InvalidArgumentException.
log() влиза в този интерфейс (LoggerInterface) но е един вид, изкуствено добавен метод, невлизащ в rfc5424, чиято цел е другите 8 метода да го извикват и реално – там да се извършва самото логване (The other eight methods are forwarding the message and context to it).

Относно съобщенията за грешките:

Every method accepts a string as the message, or an object with  __toString() method. Implementors MAY have special handling for the passed objects. If that is not the case, implementors MUST cast it to a string.

The message MAY contain placeholders which implementors MAY replace with values from the context array.

Литература:

https://www.php-fig.org/psr/psr-3/

https://datatracker.ietf.org/doc/html/rfc5424

PSR-12: Extended Coding Style

  • This specification extends, expands and replaces PSR-2, the coding style guide and requires adherence to PSR-1, the basic coding standard.
  • All PHP files MUST use the Unix LF (linefeed) line ending only.
  • All PHP files MUST end with a non-blank line, terminated with a single LF.
  • The closing ?> tag MUST be omitted from files containing only PHP.
  • There MUST NOT be a hard limit on line length.
  • The soft limit on line length MUST be 120 characters.
  • Lines SHOULD NOT be longer than 80 characters; lines longer than that SHOULD be split into multiple subsequent lines of no more than 80 characters each.
  • There MUST NOT be trailing whitespace at the end of lines.
  • Blank lines MAY be added to improve readability and to indicate related blocks of code except where explicitly forbidden.
  • There MUST NOT be more than one statement per line.
  • Code MUST use an indent of 4 spaces for each indent level, and MUST NOT use tabs for indenting.
  • All PHP reserved keywords and types MUST be in lower case.
  • Any new types and keywords added to future PHP versions MUST be in lower case.
  • Short form of type keywords MUST be used i.e. bool instead of booleanint instead of integer etc.

Всеки PHP файл трябва да следва следната стуктура:

  • започва с <?php
  • File-level docblock
  • One or more declare statements като например declare(strict_types=1);
  • Неймспесът на файла
  • use стейтмънти в следният ред – class-based use,  function-based use, constant-based use, разделени с празен ред за прегледност
  • самият код на файла
  • <?php тагът трябва да е на свой отделен ред само когато е първият такъв за файла, но не и ако например имаме вграден HTML код в PHP кода
  • import/require трябва да съдържат винаги само fully qualified пътища
  • Compound namespaces трябва да имат дълбочина, максимално 2
  • Като инстанцираме клас, на същият ред не трябва да имаме коментари. Т.е. след new Foo(); // не може!
  • The extends and implements keywords MUST be declared on the same line as the class name
  • The opening brace for the class MUST go on its own line and MUST NOT be preceded or followed by a blank line;
    the closing brace for the class MUST go on the next line after the body and MUST NOT be preceded by a blank line.
  • Ако даден клас имплементира повече от 1 интерфейс, те могат (не задължително) да са на отделни редове и да са индентирани веднъж.
  • use за трейтове трябва да е на следващият ред след отварящата скоба на класа. Ако са повече от един трейт – всеки един на отделен ред и със свое use.
  • Visibility MUST be declared on all properties, constants and methods.
  • There MUST NOT be more than one property declared per statement.
  • Property and method names MUST NOT be prefixed with a single underscore to indicate protected or private visibility. That is, an underscore prefix explicitly has no meaning.
  • abstract and final declarations MUST precede the visibility declaration.
  • static declaration MUST come after the visibility declaration.
  • след името на метода или функцията не трябва да има интервал(и), както и в скобите с параметрите след и преди отварящата и затварящата скоба. Както и трябва да има по един интервал между параметрите.
    public function fooBarBaz($arg1, &$arg2, $arg3 = [])
  • същото важи и при викане на функция или метод – bar();, а не bar ();
  • параметрите на метод или функция могат да са на отделен ред всеки, като
    ) и { след тях трябва да са на един ред.
    Както и типът на връщаният резултат, напр. ): string {
  • There MUST NOT be a space between the variadic three dot operator and the argument name – public function process(string $algorithm, …$parts)
  • при викане на метод или функция, аргументите могат да са на отделен ред като първият аргумент също трябва да е на нов ред.
  • Closures MUST be declared with a space after the function keyword, and a space before and after the use keyword.
  • и т.н…

Литература:

https://www.php-fig.org/psr/psr-12/

PSR-1: Basic Coding Standard

  • PHP code MUST use the long <?php ?> tags or the short-echo <?= ?> tags; it MUST NOT use the other tag variations.
  • Files MUST use only UTF-8 without BOM for PHP code.
  • PHP файл трябва да съдържа само декларация на клас (един клас, не повече), декларация на функции, декларации на константи или инстанцирането/извикването им но не и двете.
    This means each class is in a file by itself, and is in a namespace of at least one level: a top-level vendor name.
  • Namespaces and classes MUST follow an „autoloading“ PSR: [PSR-0PSR-4]. Важи също и за абскласове, интерфейси и трейтове.
    Всеки клас трябва да има поне вендор, който да му е първото ниво, демек, не трябва да има „хвърчащи“ класове. …and is in a namespace of at least one level: a top-level vendor name.
    A fully qualified class name has the following form:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
  • Class names MUST be declared in StudlyCaps (aka PascalCase).
  • Class constants MUST be declared in all upper case with underscore separators.
  • Method names MUST be declared in camelCase.
  • За имената на пропъртитата няма зададен стандарт, освен да се приеме един такъв и всички да са по него.

Литература:

https://www.php-fig.org/psr/psr-1/