Types of tests

Unit Testing

Unit Testing: Analysing a small piece of code is known as Unit Testing. Each unit test targets a unit of code in isolation. Unit testing should be as simple as possible, and it should not be depended on another functions/classes.

Тестваме само отделните методи на даденият клас. При подаване на някакви тестови аргументи, и знаейки какъв резултат трябва да буде върнат, виждаме дали методът работи правилно.

Не се допуска взаимодействие с какъвто и да било storage (DB, files…) както и например HTTP заявки към други компютри. Ако например ни трябва работа с DB, то фейкваме самата връзка за та не се осъществи реално работа с DB.

Всеки тест е напълно независим от останалите.

Functional Testing

Тези тестове имат цел про да симулират потребителското взаимодействие с SUT като например посредством браузър.
„Отвори този URL, събмитни дадена форма, в отговора има ли даден текст…“

Integration Testing

Много близък до Unit test тип, с разликата че работим реално с DB или друг сторидж.

Acceptance Testing

Acceptance Testing: This is the last phase of the testing process. Here we check the behavior of whole application from users side. End users insert the data and check the output whether it meets the required specifications or not. They just check the flow, not the functionality.

Допускат се взаимодействие с storage (DB, files…) както и например HTTP заявки към други компютри. Тестваме цялостното поведение на SUT от позицията на външен поребител.

Литература:

https://www.valuebound.com/resources/blog/understanding-phpunit-and-how-to-write-unit-test-cases

Things every developer absolutely, positively needs to know about database indexing – Kai Sassnowski

SOME Difference Between B-tree and Binary tree

https://www.tutorialspoint.com/difference-between-b-tree-and-binary-tree

Първа разлика: При B-Tree родителският елемент може да има N на брой наследника, а не само 2 като при Binary tree.

Втора разлика: При B-Tree всички последни елементи (листата) са винаги на едно ниво, за разлика от Binary tree. И по този начин при B-Tree за да намерим дадено „листо“ и тръгнем от root елементът, ще ни трябват винаги еднакъв брой стъпки.
И всички „листа“ при B-Tree са сортирани.
И има връзка (т.н. double linked list) между отделните „групички“ (т.н. sets of leafnodes) листа на отделните „родители“, за да не се налага да се връщаме едно ниво нагоре когато търсим, която я няма в даденият set of leafnodes.

Unit tests doubles

Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.

Stubs

Просто методите на stubs са хардкоднати да връщат винаги даден предефиниран резултат при различни сценарии. Дори не хвърлят и exceptions или каквото и да е от самата бизнес логика.

A stub provides predetermined responses to calls made during a test. For example, if testing a payment gateway, a stub can simulate both successful and failed transactions, ensuring your code responds appropriately.

Example: Your test class depends on a method Calculate() taking 5 minutes to complete. Rather than wait for 5 minutes you can replace its real implementation with stub that returns hard-coded values; taking only a small fraction of the time.

Or the network connection to twitter API is very slow, which make my test slow. I know it will return timelines, so I made a stub simulating HTTP twitter API, so that my test will run it very fast, and I can running the test even I’m offline.

Mocks

Very similar to Stub but interaction-based rather than state-based. This means you don’t expect from Mock to return some value, but to assume that specific order of method calls are made.
Example: You’re testing a user registration class. After calling Save, it should call SendConfirmationEmail.

Stubs and Mocks are actually sub types of Mock, both swap real implementation with test implementation, but for different, specific reasons.

Stubs don’t fail your tests, mock can.

Stub – for replacing a method with code that returns a specified result.

Mock – a stub with an assertion that the method gets called.

С mock например можеш да провериш дали при дадени сценарии тестваният метод ще хвърли или не exception, дали минава или фейлва например някаква валидация…
Със stub – не, там просто се връща нещо хардкоднато.

Example in JavaScript:

var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}

var Mock = {
calls: {
method_a: 0
}

method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}

Литература:

https://www.bairesdev.com/blog/stub-vs-mock

https://stackoverflow.com/questions/3459287/whats-the-difference-between-a-mock-stub

https://martinfowler.com/articles/mocksArentStubs.html

Kanban vs. Scrum

They are both Agile methodologies but…

Kanban doesn’t do sprints as Scrum does. There are no sprints, sprint plannings, sprint retrospection etc… The development is continious, like an assembly line. Constantly new and new task are coming in the backlog, the team members take them, do them and so on…

Both Kanban and Scrum are not so much different nor competing, they can be used together. One such example may be if we generally use Scrum’s sprints, planings, retrospectives, but for the part where we do the task, we may use Kanban. A kind of a hybrid – outside Scrum, inside Kanban.

Using Kanban, you as a developer should only pick a task from the Backlog, put it in Doing state, and once completed – move it in Done.

Kanban is more suitable for continiously improving given product, support, bugfixing etc… while Scrum is more suitable for solving more complex problems, usually during the initial development of the product. So, yet another example where these two methodologies may be used together: while we develop the product and deploy on production, maybe Scrum is more suitable. But later on, if the product is operational and needs bugfixing, pathes, updates (and certanly it will)… maybe Kanban would be better.

One feature of Scrum that I personally find as an advantage over Kanban

In some sence Scrum protects the developers from the constant changes and demands from the clients – lets do this too, lets do that too, change this task for it’s not good and we need smth. else…

By starting a sprint with given set of tasks and given time commitment, you actually say to the client „Look, these are the tasks, and they will be done by… some date… That’s it.“

Waterfall methodology

If we divide the entire development proces, from customer request, until product done, this methodology says tha the entire process must be divided into phases and most importantly – no phase should start before the previous is completed.

It’s pretty rigid and linear. The method relies heavily on all the requirements and thinking twice before you begin.

No phase should overlap other and each one fully depends on the previous one.

What we understand by phases? Tasks?
No, phases in general. For example:

Phase 1: Requirements from the Customer – this should be the only phase in which the Customer can take part and intervene. The requirements phase states:

  1. what the system should do;
  2. resources required for the project;
  3. what each team member will work on and at what stage;
  4. a timeline for the entire project, outlining how long each stage will take;
  5. details on each stage of the process.

Phase 2: Analysis – make sure that the Project is realistic and applicable in production, financial aspects etc…

Phase 3: Design – analyse the architecture, what technologies will be used, do we have the people for these technologies, the programming languages, the hardware and infrastructure needed…

Phase 4: Implementation – development begins…

Phase 5: Testing and Debugging

Phase 6: Operation – the products is deployed on production…

Phase 7: Maintenance, debugging, patches, updates and additional functionality…

Waterfall methodology is suitable for cases where:

  • we have fixed requirements;
  • we have specifications and documentation.

Not suitable for cases where

  • frequently changing requirements;
  • constant new requirements from the customer;
  • when updates are required ASAP.

HTTP/2

Почти всички от най-разпространените браузъри го поддържат от краят на 2015 г.

As of July 2023, 36% (after topping out at just over 50%) of the top 10 million websites support HTTP/2.

Its successor is HTTP/3, a major revision that builds on the concepts established by HTTP/2.

  • Притежава т.н. negotiation mechanism, позволяващ използването на по-стария HTTP 1.1. Create a negotiation mechanism that allows clients and servers to elect to use HTTP/1.1, 2.0, or potentially other non-HTTP protocols.
  • Напълно съвместим е с HTTP 1.1 откъм методи (GET, POST…), статус кодове, хедъри… Maintain high-level compatibility with HTTP/1.1 (for example with methodsstatus codesURIs, and most header fields).
  • По-оптимални зареждане на URL ресурсите чрез: header compression, server push (deprecated), TCP connection multiplexing, prioritization of requests.
  • It is fully multiplexed, sending multiple requests in parallel over a single TCP connection.
  • HTTP/2 is binary, instead of textual. This gives the ability for HTTP/2 to be multiplexed, meaning that it can receive multiple requests at a time over just one connection instead of having to have multiple connections to complete requests.

Използването на HTTP/2 при условие, че досега е използван HTTP 1.1, не изисква никакви промени по web приложението.
The proposed changes do not require any changes to how existing web applications work, but new applications can take advantage of new features for increased speed. HTTP/2 leaves all of HTTP/1.1’s high-level semantics, such as methodsstatus codesheader fields, and URIs, the same. What is new is how the data is framed and transported between the client and the server.

Литература:

https://en.wikipedia.org/wiki/HTTP/2

https://dzone.com/articles/understanding-http2

https://tools.keycdn.com/http2-test

https://blog.fiverr.com/post/http2-is-your-site-using-it-heres-how-you-can-check

https://en.wikipedia.org/wiki/HTTP/3

SQL vs. NoSQL databases – basics

Some basic differences between SQL and NoSQL databeses:

SQL /relational/NoSQL /non relational/
Relational database, data is structured in tables.No relational. Structured, semi-structured or unstructured data.
Rigid table structure and tabular data model.Flexible table structure and non-tabular data model. Different records may have different columns set.
Relations between tables.No relations between tables.
Data is normalized to at least 1NF.No normalization.
Stored data is table based.Stored data is document oriented or key-value pairs.
Verically scaled /more storage and calculation power needed on the same DB server/.Horizontaly scaled /multiple nodes in a cluster to handle increased workloads/.
Normalization of the data.No normalization.
Use SQL to query the database.Use varying query languages, some don’t even have a query language.
Less faster than NoSQL.Much faster than SQL, high performance, particularly read performance.
Have to have table schemas prior using it.No need to define table structure prior using it.
Not the best solution for HUGE amount of data.Best solution for HUGE amount of data.

Литература:

https://www.coursera.org/articles/nosql-vs-sql

https://www.coursera.org/articles/relational-vs-non-relational-database

https://www.datacamp.com/blog/what-is-a-graph-database

Comparing Objects in PHP

Или каква е разликата между comparison operator (==) и identity operator (===)

Comparison operator дава true, когато „Two object instances are equal if they have the same attributes and values (values are compared with ==), and are instances of the same class.
Демек, когато данновата им част (пропъртитата) са еднакви, и са различни обекти, но от един и същи клас. А както знем класът по принцип е един вид тип. И също знаем, че по принцип Comparison operator (двойното равно) не се интересува от типът на сравняваните променливи, ако са скаларни. Но очевидно, и ако са обекти.

Демек това ще върне true.

var_dump(123 == '123');

Но това ще върне false.

var_dump(123 === '123');

Identity operator от друга стана ще върне true само ако двата обекта са всъщност една и съща инстанция, демек, когато: When using the identity operator (===), object variables are identical if and only if they refer to the same instance of the same class.

Демек, едното – ако са различни обекти, но от един клас, и с еднакви properties, другото – когато са един и същ обект.

Литература:

https://www.php.net/manual/en/language.oop5.object-comparison.php

Junction tables

Junction tables are a standard practice in relational database design.

If you have a many-to-many relationship between two entities, the standard way to represent them is with three tables.

Two of the tables are entity tables, with a primary key. A junction table lies between them (logically) and contains two foreign keys, one that references each entity table. Often, these two foreign keys will be the only two columns in the junction table.

Демек, това е таблица, която осигурява логическата свързаност между други две таблици, и обикновно съдържа само техните Primary keys.

Има три типа релации в релационните бази данни:

  1. One to one, когато на даден запис от т.н. parent таблица отговаря само един запис от т.н. child таблица.
  2. One to mаny – когато на даден запис от т.н. parent таблица отговаряш N на брой записа от т.н. child таблица
  3. Many to many – когато на даден запис от т.н. parent таблица съответстват както N на брой записи от т.н. child таблица. Но също и даден запис от т.н. child таблица съответстват N на брой записи от т.н. parent таблица. За какъв практически пример се сещам…
    Имаме таблица orders и таблица order_items. Ясно е коя какво съдържа – за дадена поръчка – N на брой items. Но всеко от тези items на дадената поръчка, могат да са част и от друга поръчка.

По принцип, many to many релация става с точно такава таблица. Не знам как можеш да обединиш две таблици в many to many релация директно. То по принцип тази релация не на практика директно непостижима в релационните бази, всичко се разлага винаги до one to many така или иначе.

The rel attribute of the link tag

<link href="main.css" rel="stylesheet">

This simple example provides the path to the stylesheet inside an href attribute, and a rel attribute with a value of stylesheet. The rel stands for „relationship“, and is one of the key features of the <link> element — the value denotes how the item being linked to is related to the containing document.

The ralationship with the document, това е ключовата дума. Демек, какво е – CSS, икона, JS…

In HTML, link types indicate the relationship between two documents, in which one links to the other using an < a >, < area >, < form >, or < link > element.

Литература:

https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel

Що е HTTP Authentication и има ли то почва у нас?

Нека изходим от началната точка – искаме HTTP аутентикация. Стандартният начин е чрез cookies. Но самият HTTP протокол предлага и чисто свой начин.

HTTP аутентикацията е процес, минаващ през серия от рекуести и респонси, които най-просто могат да се илюстрират така:

Първо клиентът отправя HTTP заявка.

На която сървът отговаря „не давам без да се аутентикираш кой си“, чрез хедърът WWW-Authenticate и статус кодът 401. Само един от тези (хедър и код) достатъчен ли е? То няма как. Един HTTP респонс от какво се състои? От status line, хедъри и евентуално body. Нещо такова:

HTTP/1.1 200 OK
Content-Type: text/plain

This is the response body bla bla bla

Демек, няма как да отговорим само с код или само хедър, респонсът е нещо цяло, атомарно, което трябва да има и двете, такъв е протоколът.

HTTP дава два начина за аутентикация – Basic и Digest.

Basic е по-простият начин, сървът изпраща към клиента хедърът WWW-Authenticate и статус кодът 401 като с директивата realm (задължително) задава и за кой realm иска юзър и парола. Например:

WWW-Authenticate: realm="Videos"

Клиентът има юзър и парола, които трябва първо да конкатенира с двуеточие, например alfons:123123, след което полученият стринг да бъде Base64 енкоднат и изпратен на сърва с хедъра Authorization. Например:

Authorization: Basic YWxmb25zOjEyMzEyMw==

Digest е подобен но предлага доста повече сигурност като например, че паролата е хеширана, има ограничение на броя опити, повече директиви и т.н…

Що е HTTP realm и има ли то почва у нас?

Какво наистина е т.н. realm? Целият сайт? Даден ресурс (URL)?…

И да, и не.

A realm can be seen as an area (not a particular page, it could be a group of pages) for which the credentials are used; this is also the string that will be shown when the browser pops up the login window

According to the RFC 7235, the realm parameter is reserved for defining protection spaces (set of pages or resources where credentials are required) and it’s used by the authentication schemes to indicate a scope of protection.

Демек, един и ли група от ресурси, които са групирани по защитата им, демек – трябва ти аутентикация + ауторизация за да можеш да ги достъпиш.

Могат да се застъпват, демек като множества, между които даден ресурс може да присъства и в повече от един realm. Просто като един вид групиране на ресурси.

Демек, realm не значи непременно цял сайт или отделна страница, въпреки че може и да значи. По скоро група от страници или подобни ресурси, групирани според това кой (authentication) и как (authorization) може да ги достъпва.

Литература

https://stackoverflow.com/questions/12701085/what-is-the-realm-in-basic-authentication

Host HTTP header

При request от стрна на брауза към даден хост, например www.alabala.com, браузът, като клиент, добавя и request header Host, съдържащ името на желаният домейн, понеже на даден сърв може да има N на брой виртуални хоста. Например:

Host: www.alabala.com

Идеята на този хедър е даденият сърв да знае кой точно домейн да обслужи.

В какъв смисъл?

В смисъл, че DNS системата ще превърне това www.alabala.com в IP адрес за да свърже брауза със сърва. Но сървът ще получи един HTTP рекуест и понеже сървът обикновено хоства много сайтове (виртуални хостове), няма да знае за кой точно от тях е даденият рекуест.

Ето затова е този хедър, за да кажеш на сърва кой точно домейн искаш.

Не е ли само DNS достатъчен?
Не е, работата на DNS е да обърне www.alabala.com в IP адрес за да може клиентът чисто от мрежова гледна точка да намери сърва, да си изпрати TCP пакетите с рекуеста и да получи респонса…

When multiple websites or applications are hosted on one server, this is known as virtual hosting. The server has a single IP address in this scenario, and received requests are routed to the relevant domains.

Given that websites and applications don’t have their own personal IP addresses, the purpose of the host header is to provide the server with information about the proper recipient of the request located downstream.

Да се свържеш с даден домейн с помощта на DNS значи да се свържеш по IP с даденият сърв, но ти се свързваш със сърва чисто на мрежово ниво, не знаеш на този сърв кой точно домейн ти трябва. Все едно да се стовариш с куфара на дадена гара и да не знаеш на кой точно адрес да отидеш.
Не е задължително всеки сайт да има собствен IP адрес.

Литература

https://crashtest-security.com/invalid-host-header/

HTTP/2 Server Push deprecated

До колкото разбрах, този feature е депрекейтнат.

Защо?

Явно са решили, че е безсмислено да се действа на принципа „щом искаш това, на ти и това, и това, и това…., защото ще ти трябват и те“. Нали все пак трябва някакъв алгоритъм, по-който да се реши точно какво допълнително ще ти трябва. Което не е лесно. Алгоритъмът демек.

Демек, реално идеята на HTTP/2 Server Push e супер, но на практика е много трудна и несигурна за реализация – сърва да реши кое допълнително ще ти трябва, отделно – кое вече имаш кеширано и не трябва да ти го праща и т.н…
Отделно, дали няма да флууднеш, да залееш клиента с лавина от респонси, които уж ще му трябват, която лавина той ще е затруднен да обработи…

Отделно, в HTML имаме таг атрибут rel=“preload“ към тагове като <link…> с който задаваме на брауза да започне да тегли даденият ресурс още преди да е ренднал и дисплейнал дадената страница, с което получаваме почти същият ефект.
Така даваш възможност на брауза, бидейки клиент, да прецени дали го има вече този ресурс и съответно дали да го тегли отново.

Литература:

Що е Mojibake и има ли то почва у нас?

От японски „moji“ – символ, буква,
и „bakeru“ – деформирам, окепазявам.

Да си припомним, че браузърът или по-общо казано web client-ът получава respons-ът от сърва като „купчина единици и нули“. Която купчина единици и нули, той трябва да знае как да я визуализира, и изобщо как да я използва.

По принцип сървът изпраща в отговорът на даденият рекуест, като част и HTTP хедърът Content-Type, с който показва на клиента за какъв тип контент иде реч – текст, изображение…

Ако е изображение ОК, клиентът ще го визуализира, стига да знае графичният формат – JPEG, GIF, PNG…, за което трябва също да има съответният HTTP хедър Content-Type.

Но ако е текст, правилното визуализиране зависи и от това клиентът да знае с какъв енкодинг е кодиран въпросният текст. В смисъл, че една и съща буква, например латинско „W“ или кирилско „Щ“, може в един енкодинг да има един двоичен код, в друг енкодинг – друг, и т.н. „глиф“ (самата буква) един път да е една, друг път на… на Бог знае каква азбука.

Ако се случи ситуация клиентът да декодне (демек визуализира) въпросният текст с грешният енкодинг, това е mojibake, демек – т.н. „гарги“ или текст, написан уж на една азбука и език, а показан на друга.

Затова понякога се случва текст например на български и на кирилица, да излиза на „китаица“, „японица“ или „аборигеница“, и трябва в менюто на браузъра да задаваме енкодинга ръчно.

Литература:

https://en.wikipedia.org/wiki/Mojibake

https://en.wiktionary.org/wiki/mojibake

Some of the PHP 8.2 new features

Readonly classes

До сега можеше да декларираме отделни пропъртита на даден клас като readonly.
Вече може всички пропъртита да ги декларираме readonly ако декларираме целият клас readonly.

Вместо:

class MyClass
{
	public readonly string $myValue,
	public readonly int $myOtherValue
	public readonly string $myAnotherValue
	public readonly int $myYetAnotherValue
}

това:

readonly class MyClass
{
	public string $myValue,
	public int $myOtherValue
	public string $myAnotherValue
	public int $myYetAnotherValue
}

Важно е, че в такъв случай, когато целият клас е readonly, всичките му пропъртита трябва задължително да имат тип, дори и да е mixed.

Също и, че не може да се декларират като readonly – enum, interface и trait. Само класове.

Dynamic class properties

Вече не може да се използват клас пропъртита, които не са декларирани.

From PHP 8.2 onwards, dynamic properties are deprecated. Setting a value to an undeclared class property will emit a deprecation notice the first time the property is set.

However, from PHP 9.0 onwards, setting the same will throw an ErrorException error.

…if you want to stop these deprecation notices after upgrading to PHP 8.2, you can use PHP 8.2’s new #[AllowDynamicProperties] attribute to allow dynamic properties on classes.

new standalone types

Вече имаме типове като true, false и null, демек такива, които са по принцип values, a не типове, могат вече да се използват и като типове. Демек, функция например може да ти връща тип null, или тип false, или тип true.

Но да не забравяме, че true и false са union type на bool. Демек, не можем да декларираме параметър или тип на връщана стойност като например bool|false.
Both true and false types are essentially a union type of PHP’s bool type. To avoid redundancy, you cannot declare these three types together in a union type. Doing so will result in a compile-time fatal error.

Disjunctive Normal Form (DNF) Types

sensitive parametters

Има случаи, в които волно или неволно можем да логнем например пароли, в различни log файлове или подобни по предназначение системи.

За да избегнем това програмно, ако подобна информация е например клас пропърти или параметър на функция, можем да ги декларираме като т.н. SensitiveParameter с помощта на атрибутът [\SensitiveParameter] по следният начин.

function example(
    $ham,
    #[\SensitiveParameter] $eggs,
    $butter
) {
    throw new \Exception('Error');
}

example('ham', 'eggs', 'butter');

/*
Fatal error: Uncaught Exception: Error in test.php:8
Stack trace:
#0 test.php(11): test('ham', Object(SensitiveParameterValue), 'butter')
#1 {main}
thrown in test.php on line 8
*/

When you generate a backtrace, any parameter with the \SensitiveParameter attribute will be replaced with a \SensitiveParameterValue object, and its real value will never be stored in the trace. The SensitiveParameterValue object encapsulates the actual parameter value — if you need it for any reason.

Fetch enum Properties in const Expressions

https: // kinsta.com/blog/php-8-2/#fetch-enum-properties-in-const-expressions

Allow Constants in Traits

Вече и в traits може да се дефинират константи.

trait Foo {
    public const FLAG_1 = 1;
    protected const FLAG_2 = 2;
    ...
Deprecate Partially Supported Callables

Първо, какво значи „partially supported callables“?

These callables are termed „partially supported“ because you cannot interact with them directly via $callable(). You can only get to them with the call_user_func($callable) function.

From PHP 8.2 onwards, any attempts to invoke such callables — such as via call_user_func() or array_map() functions — will throw a deprecation warning.

Resource Description Framework (RDF)

Метаданни или мета информация – това са данни за данните, или информация за информацията.

На практика, компютрите са едни електронни сметалки, съхраняващи и обработващи информация по дадени алгоритми, зададени под формата на програми.

За да могат компютрите по-ефективно (в смисъл по-информативно) за нас потребителите, да обработват въпросната информация, те трябва по техен начин, да „знаят“ повече за нея. Тя, информацията, да е „по-разбираема“ за тях.

В какъв смисъл „по-разбираема“ и „да знаят“?

Цялата информация в Интернет е двоична (текст, изображения, звук, видео….). Което чисто семантично е напълно непонятно за компютъра, освен ако нямаме изкуствен интелект, защото пак да кажем, компютрите са просто едни електронни сметалки, които съхраняват информацията и могат да я обработват само по програмно зададен алгоритъм.

Идеята на Semantic Web най-просто казано е, да зададе повече информация за самата информация (мета информация), което от своя страна да помогне на компютъра и неговите програмно заложени алгоритми, да ни предоствят по-информативни услуги.

В какъв смисъл „по-информативна“?

В смисъл, че когато въпросните „електронни сметалки“ „знаят“ повече (мета инфо) за информацията, която съдържат, те ще могат да ни я предоставят по-близко до това, което всъщност търсим. Демек, ще бъдат по-информативни.

За създаване на разбираем от компютрите език, се използва форматът RDF (Resource Description Framework), който е на основа на синтаксиса на XML и използва идентификатори URI за обозначение на ресурсите.

RDF — това е система за описание на мрежовите ресурси, понятна за компютъра. Форматът RDF е предназначен за съхранение на метаданните. RDF не е предназначен за четене от човек.

URI vs. URL vs. URN

URI е обединяващ термин, демек нещо служещо да идентифицира даден web ресурс по един или друг начин. Какъв „един или друг“ начин? И двата (URN и URL) служат за да сочат към да даден ресурс в дадена мрежа, уникално но на ниво мрежа, била тя малка и локална, или глобалната Интернет.

И двата са разновидности от т.н. URI – идентификатор на даденият ресурс, по един или друг начин.

Това може да стане или с помощта на даден уникален стринг URN (RFC 2141),
или стринг, сочещ как да достъпим този ресур в дадената мрежа – URL (RFC 3986).

Разликата между URN и URL e, че URN е един вид само текстов идентификатор, демек стринг, който не води никъде в мрежата, а по-скоро се използва в например XML документи, разни парсери и т.н…
Демек, URN не води никъде, неговата идея е да е един стрингов идентификатор и се използва предимно в XML документи например…

Сподед RFC 2141 един примерен URN би бил:
urn:isbn:123326546546
urn:EGN:7804111111
urn:мекици:топли:предпочитани:с малиново сладко

Това което ги обединява е URI, който има малка практическа полза, защото и URN, и URL са напълно достатъчни за уникално идентифициране на даден ресурс в дадена мрежа. Просто по различен начин – едното не води на никъде, другото води, но и двете трябва уникално да идентифицират даден ресурс.

Въпреки, че например можем да използваме стринг, формиран като URL да бъде използван за да идентифицира уникално даден ресурс, но да не води на никъде. Демек, няма да е задължително URL, а ще е на практика URN. Да не забравяме, че и URL, и URN са подмножества на URI, демек негови разновидности или ако щете – практически превъплъщения.

Демек, чуем ли URN или URL, все едно чуваме URI но в различен формат.

Литература:

https://en.wikipedia.org/wiki/Web_resource

Tell, don’t ask

Идеята на този OOP принцип е да знаеш предварително даден обект какво може да прави като функционалност, и просто да извикваш на готово тази функционалност. А не предварително да го питаш „можеш ли да свършиш това. И ако да, свърши го.“

Казваш „направи това“ и даденият обект вече сам решава дали и как да го направи.

Как точно програмно?
Няма значение, по принцип говоря. Може да е например, с проверяване на стойността на пропърти, с извикване на метод на даденият клас…
Но това проверяване на пропъртита (asking, „питането“ дали можеш да го свършиш) не трябва да става „отвън“, а вътре в класа, тоест – чрез негов метод.

Демек, разсъждавайте на пинципа, че всички пропъртита на даденият клас НЕ са public.

Защо да не са public?
Не че ще свърши светът ако са public, но по принцип, с оглед на data encapsulation, е по-добре данновата част на класа да НЕ е public.

Демек, не го питай първо „можеш ли да свършиш това“, като му използваш пропъртитата, защото това най-малкото значи, че въпросните пропъртита трябва да са public. Което значи, че намаляваш капсулацията, въпреки че можеш да го сториш и през „getters“…

Просто му кажи направи това или онова, и нека той, обектът, да си проверява вътрешно, дали може, иска или трябва да го направи това въпросното нещо.

Или например, да имаш даден огромен обект, в който например с IF-ове да са заложени различни функционалности, и ти да му викаш даден метод и чак тогава да проверяваш коя от всичките функционалности по-точно, да му извикаш.

Демек, можем да имаме огромен обект Животно, в който да имаме метод doSound() в който да проверяваме куче ли е, котка ли е, лъв ли е, кандидат-депутат в предизборна кампания ли е…. и тогава въпросното Животно да издава съответният си свой звук.

Демек:

if ($animal->type === 'dog') {
    echo 'bau bau';
} elseif ($animal->type === 'cat') {
    echo 'miau miau';
}
....

a по-скоро:

$animal->doSound();

като обектът $animal ще е обект от клас Kуче, клас Котка, клас Кандидат-депутат в предизборна кампания… и прочее Животни.

Демек, вместо първо да го питаме „що за животно си“, и тогава „аха, куче си – лай тогава“, „аха, котка си – мяукай тогава“ и т.н…. можем и да имаме отделни класове, наследяващи Животно, като например клас Куче, клас Котка и т.н… и да му казваме „doSound()“.

Искаш да нахраниш Животното, би било то от най-нисшето, до най-висшето?

ОК, но всяко животно яде определен тип храна. Тревопасните – растения, хищниците – месо…, програмистите – бира и хамбургери….

Също, преди да нахраниш даденото животно, то трябва да е гладно. Но за да определиш дали е гладно, зависи от самото животно. Всяко си има свои критерии. Ето още една „per-animal“ специфичност…. Която трябва да се отдели в класът, който е за самото животно. А не с IF-ове да се проверява кое животно е, че тогава дали е гладно….

Демек, викаш Kуче->eat() или Koтка->eat(), Програмист->eat()…, и вече в конкретният eat() метод на съответният клас, се решава дали даденото Куче, Котка, Програмист… е гладен, по свой си начин. Per animal…

Kaзваш му „яж“, и то да си решава дали да яде или не, и какво да яде.

Демек, не питай даденият обект „можеш ли да свършиш това“.
Кажи му да го свърши и нека той сам реши дали може и трябва, и иска да го свърши.

Цялата работа е да не се използват пропъртита отвън за разни проверки дали да свършим това или онова, защото по принцип е добре данновата част на класа (пропъртитата) да са private/protected, а да казваме на обекта на класа „свърши еди си какво“ и той да си знае сам.

Без да те интересува the internal state of the object. Не питай обекта за данните му, кажи му какво да прави. И нека той си знае сам работата.

Rather than asking an object for data and acting on that data, we should instead tell an object what to do

Литература:

https://martinfowler.com/bliki/TellDontAsk.html

_GET/_POST аргументите се пренабиват от дясно на ляво

Демек, следващият със същото име пренабива предишният от дясно на ляво.

localhost/i.php?aaa=111&bbb=222&aaa=333

array(2) {
    ["aaa"]=> string(3) "333"
    ["bbb"]=> string(3) "222"
}

За POST заявки – аналогично, следващият аргумент със същото име пренабива предишният.

Съвременни проблеми при бубулацията на PHP грешките

Да видим какво пише тук:

As with normal exceptions, these Error exceptions will bubble up until they reach the first matching catch block. If there are no matching blocks, then any default exception handler installed with set_exception_handler() will be called, and if there is no default exception handler, then the exception will be converted to a fatal error and will be handled like a traditional error.

An exception handler handles exceptions that were not caught before. It is the nature of an exception that it discontinues execution of your program – since it declares an exceptional situation in which the program cannot continue (except you catch it).

Ако такъв блок не бъде намерен, се надяваме, че поне глобално имаме зададен exception handler с помощта на set_exception_handler()

Ако и такъв хендлър нямаме зададен, ще имаме fatal error.

Демек, хвърлен exception трябва да бъде прихванат задължително!

Добър въпрос е ако вече и в set_exception_handler() възникне грешка, дали ще се изпълни хендлърът, зададен с set_error_handler()?

Да, ще се. Нека разгледаме следният пример:

set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline,): bool {
    echo "Error has occurred: ", $errstr, PHP_EOL;

    /**
     * It is important to remember that the standard PHP error handler is completely bypassed
     * for the error types specified by error_levels UNLESS the callback function returns false.
     * Демек, ако върнем FALSE за даденият тип грешка, това ще значи,
     * че обработването и (на този тип грешка)
     * ще продължи И КЪМ стандартният обработчик.
     * Демек, върнем ли TRUE - до там! Няма да използваме и стандартният обработчик.
     * Но пак да кажем - до там само за даденият тип грешка.
     */
    return false;
});

set_exception_handler(function (Throwable $ex): void {
    echo "Uncaught exception: ", $eXd->getMessage(), PHP_EOL;
});

throw new Exception('Catch me if you can.');

echo "Not Executed" . PHP_EOL;

Имаме ли грешка в set_exception_handler(), демек по време на хвърляне на ексепшън, ще влезем и в set_error_handler()

Class constants can override each other

class Parend
{
    public const JJJ = 123;
}

class Chilt extends Parend
{
    public const JJJ = 456;

    static public function getJjj(): void
    {
        var_dump(self::JJJ);
    }
}

Chilt:: getJjj();    // int(456)

Е, ако родителската констата е private, резултатът пак ще е 456 но не в следствие на overrid-ване, а защото Child::JJJ ще се смята за напълно отделна константа, предвид видимостта и.

Статични и динамични callbacks

Първо нека си припомним какво е callback function.
Callback function is any reference to executable code that is passed as an argument to another piece of code.
Демек, код който се предава на друг код, под формата на аргумент, за да бъде изпълнен.

А относно статични и динамични callbacks, имаме две положения:

DYNAMIC Anonymous Functions

Когато имаме анонимна функция В КЛАС, това значи че $this променливата e ДОСТЪПНА в тази анонимна функция, демек става част от скоупа му, защото „the default behavior is that the class is automatically bound to the closure“.

Eто следният пример:

<?php

class Foo
{
    public function bar(): \Closure
    {
        return function (): void {
            var_dump($this);
        };
    }
}

$foo = new Foo();
$foo->bar()();

Първо, кой е callback-ът в даденият пример? Това е методът bar(). Защо? Защото какво връща той? Връща код, не конкретна стойност. Който код, може да бъде предаден като аргумент на друг код.

Второ, var_dump()-ът ще дъмпне самият обект – object(Foo)#1 (0) {}

Което очевидно значи, че щом даденият callback е динамичен то за всеки обект от класа Foo, ще имаме достъп до обектната променлива ($this), указваща към този даден обект, в който сме към даденият момент.

Static Anonymous Functions

Нека разгледаме горният код, но с една малка разлика – callback функцията нека е статична.

<?php

class Foo
{
    public function bar(): \Closure
    {
        return static function (): void {
            var_dump($this);
        };
    }
}

$foo = new Foo();
$foo->bar()();

Ще имаме Fatal error: Uncaught Error: Using $this when not in object context…

Вече, ако имаме статично пропърти на класа или статичен метод, можем да го достъпим, но чрез self

<?php
class Foo
{
    private static string $a = 'Hello world';

    public static function bar(): \Closure
    {
        return static function (): void {
            var_dump(self::$a);
        };
    }
}

$foo = new Foo();
$foo->bar()();      // Hello world

Литература:

https://en.wikipedia.org/wiki/Callback_(computer_programming)

https://www.designcise.com/web/tutorial/what-are-static-anonymous-functions-in-php

Iterator

<?php

interface Iterator extends Traversable
{
    public function current(): mixed;
    public function key(): mixed;
    public function next(): void;
    public function rewind(): void;
    public function valid(): bool;
}

Вграден интерфейс, който може да направи цикленето с foreach по-гъвкаво и къстъмизирано. Демек, да може да се задават определени действия за всяка итерация, задължавайки итериращият клас да имплементира тези действия.

Демек, ако имаме например обект, който имплементира този интерфейс, и го изциклим с foreach, при всяка итерация, в определен ред, ще бъдат извикани гореспоменатите методи.

Нека разгледаме следният пример:

<?php

class MyIterator implements IteratorInterface
{
    private int $position = 0;

    private array $array = array(
        "firstelement",
        "secondelement",
        "lastelement",
    );

    public function __construct()
    {
        $this->position = 0;
    }

    public function rewind(): void
    {
        var_dump(__METHOD__);
        $this->position = 0;
    }

    public function current(): mixed
    {
        var_dump(__METHOD__);
        return $this->array[$this->position];
    }

    public function key(): mixed
    {
        var_dump(__METHOD__);
        return $this->position;
    }

    public function next(): void
    {
        var_dump(__METHOD__);
        ++$this->position;
    }

    public function valid(): bool
    {
        var_dump(__METHOD__);
        return isset($this->array[$this->position]);
    }
}

$it = new myIterator;

foreach ($it as $key => $value) {
    var_dump($key, $value);
    echo "\n\n";
}

Имаме обект, имплементиращ въпросният интерфейс, и го циклим.

string(18) "myIterator::rewind"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(0)
string(12) "firstelement"


string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(1)
string(13) "secondelement"


string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(2)
string(11) "lastelement"


string(16) "myIterator::next"
string(17) "myIterator::valid"

Вижда се ясно в какъв ред автоматично се викат отделните методи.

Просто казано, идеята е да можем по свой начин, чрез всеки метод да зададем какво да се случва при всяка итерация.

Литература:

https://www.php.net/manual/en/class.iterator.php

Text-based web browser

text-based web browser is a web browser that renders only the text of web pages, and ignores most graphic content.

Under small bandwidth connections, usually, they render pages faster than graphical web browsers due to lowered bandwidth demands.

Additionally, the greater CSSJavaScript and typography functionality of graphical browsers require more CPU resources.

Практическата им полза е доста ограничена но могат да се употребяват например при различни компютърни рийдъри, или за люде със слабо зрение…

Един известен пример за text based web browser е например Lynx.

Progressive enhancement e идея, която позволява по-широкото използване на text-based web browsers. Защо? Защото идеята е една уебстраница, бидейки се състояща се от три основни елемента: мултимедия, CSS, JavaScript и разбира се текст, от нея да може да бъде използван например само текстът, демек – само най-ниското ниво от „пирамидата“, най-същественото.

Oт тази пирамида виждаме, че в основата на всяка уебстраница, демек – най-същественото, е самият текст.

След това идва CSS – визуалната част на даденият текст, което разбира се включва оформяне както на самият текст, така и на мултимедийните части на страницата. Но идеята е като цяло – форматиране на визуалната част.

И най-горе – функционалната част, JavaScript, която добавя интерактивност и програмируемост на поведението на уеб-браузърът, заредил дадената уебстраница.

Просто казано, това е идея една уебстраница да се прави така, че тръгвайки от горе надолу, да може безпроблемно да бъде визуализирана както на по-слаби браузъри, на по-слаби връзки, така и по принцип да се визуализира и използва само част от самата уебстраница, трвъгвайки от горе надолу.

Предишната подобна идея за организиране на уебстраници е т.н.  graceful degradation, при която уебстраницата се прави пригодна за най-новите страндарти и браузъри, НО с идеята да е използваема и от по-старите такива.

The strategy is an evolution of a previous web design strategy known as graceful degradation, wherein Web pages were designed for the latest browsers first, but then made to work well in older versions of browser software. Graceful degradation aims to allow a page to „degrade“ – to remain presentable and accessible even if certain technologies expected by the design are absent.

Демек, в единият случай мислим нещата от долу нагоре, а в другият – от горе надолу.

Литература:

https://en.wikipedia.org/wiki/Text-based_web_browser

https://en.wikipedia.org/wiki/Progressive_enhancement

https://en.wikipedia.org/wiki/Fault_tolerance

Lightweight markup language

Това са маркъп езици със силно опростен синтаксис, обикновено използвани за просто форматиране на текст, например като за документация…

Визуалната им функционалност се ограничава само до текстово форматиране като:

  • размер на шрифта;
  • удебеляване;
  • италик;
  • табулация, маргини…
  • и т.н… но все чисто текстов форматинг.

Общо взето lightweight markup language служат основно за чисто текстово форматиране, и с оглед основно за писане на текстова документация.

Също и да можеш кто погледнеш суровият текст, да можеш да прочетеш самият текст. „Lightweight markup languages are used in applications where it may be necessary to read the raw document as well as the final rendered output„.

__call сработва не само по име, но и по видимост

<?php
class Jazz
{
    public function __call(string $method, array $args): int
    {
        echo 'Majic ';
        return array_sum($args) * 2;
    }

    private function multiply(int $arg): int
    {
        echo 'No majic ';
        return $arg * 2;
    }
}

$class = new Jazz;
echo $class->multiply(3);      // Majic 6
echo PHP_EOL;
echo $class->multiply(15, 5);  // Majic 40

Convention over configuration

Принцип в софтуерният дизайн, при който се стремим колкото се може повече неща, които иначе се задават чрез напримен конфиг файлове и прочее конфигурации… да се подразбират сами.

Например, имаме модел Users, зад който имаме таблица в базата, наречена users. Ето един пример за Convention over configuration – вместо да имаме конфиг-настройка, приемаме че щом моделът се казва така, и таблицата защо да не се казва така.

Явно идеята на този принцип е да се използват еднакви имена, за да се намали факторът конфигуриране.

Оpaque (нищо не значещ) string

Понятие, означаващо символен низ, чието значение няма нужда да знаем, а по-скоро го използваме с условни цели. Например – като ID, като някакъв идентификатор…

Явно затова му казват „opaque“, демек непрозрачен, тоест служебен, протоколен, не ни интересува конкретното му значение. Просто го използваме за други цели без да ни интересува той самият какво значи.

Или както се казва: „do not assume that the structure of the string carries any semantic meaning“.

CORS (Cross-Origin Resource Sharing)

https://www.keycdn.com/support/cors

Дали един хост да може да използва ресурси от друг хост. Демек, дали например http://hello.com да използва например картинки от http://world.com

CORS e един вид политика от страна на браузъра (т.н. Same Origin Policy). Дали той като види, че даден сайт иска да зареди даден ресурс (css, js, image) от друг сърв, дали този друг сърв иска да му го предостави.

Разсъждавайте за една уебстраница или изобщо уеб ресурс, като нещо комплексно. В смисъл, съставено от много компоненти, като освен самият HTML код, JSON код, така и от сума ти статични ресурси, като JS, CSS, изображения…

Които могат да бъдат зареждани от различни сървъри, но искат ли въпросните сървъри да им използват въпросните ресурси?

Литература:

variables_order and request_order

„variables_order“ PHP директивата задава на кои суперглобални масиви да бъдат зададени стойности. Задаваме кои от GET, POST, COOKIE, ENV и SERVER ще презапишат една друга.

This directive determines which super global arrays are registered when PHP
starts up. G,P,C,E & S are abbreviations for the following respective super globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty paid for the registration of these arrays and because ENV is not as commonly used as the others, ENV is not recommended on productions servers. You can still get access to the environment variables through getenv().

Демек, използването на „E“ не се препоръчва, защото натоварва сървъра с още един масив, което е безсмислено, при условие че стойностите му могат така или иначе да се вземат с getenv()

А ако искаме да зададем РЕДЪТ НА ПРЕЗАПИСВАНЕ, това става с директивата „request_order“, която задава въпросният ред на презаписване.

Ето един пример:
Ако имаме request_order = „GP“, това значи, че ако имаме GET и POST променливи с еднакви имена, то стойността на дадената POST променлива ще презапише тази на съответната GET променлива.

Демек, редът на презаписване е – десните презаписват левите.

Какво, ако не зададем стойност на тази директива – „request_order“?
Leaving this value empty will cause PHP to use the value set in the variables_order directive. It does not mean it will leave the super globals array REQUEST empty.
Демек, ще използва стойността на директивата „variables_order“ и пак ще имаме $REQUEST масив със стойности.

Друго, което се вижда, че информацията в ENV e аналогична с тази, получена от getenv().

А разликата между ENV и SERVER e, че първото съдържа информация по-скоро за операционната система, както и такива, зададени от командният ред, такива, зададени с putenv()

А SERVER- променливи на ниво конкретен уебсърв, apache, nginx…

Литература:

https://www.php.net/manual/en/reserved.variables.request.php

https://stackoverflow.com/questions/43157933/what-is-the-request-precedence

.htaccess

Най-просто, целта и идеята на .htaccess файла е да оверрайдне определени настройки на Apache на ниво директория, и рекурсивно – на нейните поддиректории. Но за да кажем на Apache да използва даден .htaccess файл, трябва да използваме AllowOverride директивата.

Ако зададем стойността и на All ще разрешим .htaccess да презаписва всички настройки на ниво директория

Ако вместо All изброим дадени директиви, ще зададем, че само те ще бъдат презаписвани от даденият .htaccess.

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/

HTTP requests and response format

HTTP requests, and responses, share similar structure and are composed of:

  1. start-line, (още известен и като request line) describing the requests to be implemented, or its status of whether successful or a failure. This start-line is always a single line.
  2. An optional set of HTTP headers specifying the request, or describing the body included in the message.
  3. A blank line indicating all meta-information for the request has been sent.
  4. An optional body containing data associated with the request (like content of an HTML form), or the document associated with a response. The presence of the body and its size is specified by the start-line and HTTP headers.

Литература:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages

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/