Nowości w PHP 7.4

Premiera nowej wersji PHP 7.4 w listopadze, ale myślę, że warto już teraz zastanowić się nad tym co nas czeka tej jesieni. Może nie będzie to skok porównywalny do przejścia od wersji 5.6 do 7.0, jednak na kilka nowych "ficzerów" wyczekuje z radością. Będą to między innymi:

  • Możliwość deklaracji typów już na poziomie deklaracji klasy. Jest to rozwinięcie zapoczątkowanej od php 7.0 funkcjonalności pozwalającej zadeklarować typ dla typów prostych (string, integer, float ...itp.). Zatem komentarze w kodzie takie jak: @param, @return będą mogły pójść w zapomnienie.
  • Skrócona składnia dla domknięć (ang. closures)
    tj. z function ($x) use ($arr) { return $arr[$x]; } do fn($x) => $arr[$x].
  • Null Coalescing Assignment Operator czyli umożliwienie wpisania wartości domyślnej dla zmiennej, gdy nie posiada ustawionej żadnej wartości (taki ukryty isset()).
  • Aktulizacja dla Spread operator o możliwość wypakowania całej tablicy w jednej instrukcji.
<?php
$vine_veges = ['cucumber', 'pumpkin'];
$ground_veges = ['carrots', 'potatos'];

print_r(['eggplant', ...$vine_veges, ...$ground_veges]);

/*
Array
(
    [0] => eggplant
    [1] => cucumber
    [2] => pumpkin
    [3] => carrots
    [4] => potatos
)
*/
z https://www.pixelite.co.nz/article/new-features-in-php-7-4/ 

Jak widać, jest na co czekać, więcej informacji
https://wiki.php.net/todo/php74
https://www.pixelite.co.nz/article/new-features-in-php-7-4/

Efektywne uczenie się programowania

Inspiracją do powstania tego dzisiejszego wpisu jest poniższy artykuł

https://poznajprogramowanie.pl/warstwowy-model-nauki-programowania/

Kiedy chodziłem do szkoły nauka nie wydawała się taka przyjemna, a przecież była konieczna. Brakowało mi w tym wszystkim nie tylko celu, motywacji, ale też i sensu. Z programowaniem było jednak inaczej. Już od czasów gdy mój pierwszy komputer z Windows 95 na pokładzie i modemem 56 Kbps (https://www.pcworld.pl/news/Demony-szybkosci-modemy-56-Kbps,297544.html) zagościł w domu, pojawiła się ciekawość, a za tym i motywacja. Właściwie od tego czasu, stale poszukuje sposobów na bardziej efektywne przyswajanie nowego materiału, czy to nowych technologi, idei, wzorców, algorytmów, rozwiązań etc. ..a przecież jak widać trochę tego jest, szczególnie w naszej branży IT. Jak zatem niepogubić się w tym wsystkim i się rozwijać ? Jak to ujął autor,

"nie uczy się ani zbyt głęboko, ani zbyt płytko"

To trudne, zwłaszcza w IT, gdzie musimy równoważyć teorę i praktykę. Wydaje się jednak dobrym pomysłem by z jednej strony poszerzać informacje o tym co dzieje się w całej branży, ale też jednocześnie specjalizować się w konkretnej dziedzinie, która nas kręci. Przytoczony "Warstwowy model nauki programowania" Przemka Smyrdka przyporządkowuje aktywności do konkretnych faz uczenia się programowania przy użyciu metafory lejka sprzedaży. Zaczynamy od "Świadomości", tutaj przegląd nowinek w Social Media, udział w konferencjach, meetupach czy ogólnie rozmowach o programowaniu. Ta kategoria cechuje się niskim stopniem zaangażowania, za to wysoką ilością poznanych tematów. Później mamy takie kategorie jak "Zainteresowanie", "Porównanie", "Ewaluacja", "Działanie" a lejek kończy "Promowanie", czyli dzielenie się wiedzą z innymi. "Promowanie" jest kategorią z dużym stopniem zaangażowania i jest ograniczona do konkretnych tematów. Zdaniem twórcy taki model pozwoli nam usystematyzować nasz system uczenia się, a zatem pomoże podnieść efektywność ;-)

Ja bym jeszcze do tego dorzucił odpuszczanie ( https://dominikjuszczyk.pl/2019/06/110-odpuszczanie/ ). Nie ma co robić sobie wyrzutów, że się wszystkiego nie wie.

Microsoft SQL i docker i...Linux ;-)

Przyznaje, że tytuł może się wydawać dosyć prowokacyjny, ale nie da się zaprzeczyć, że Microsoft coraz bardziej otwiera się na Linuxa. Wydaje się, że obecny kurs Microsoftu to odejście od traktowania go niczym "raka" (https://pl.wikiquote.org/wiki/Steve_Ballmer). Więcej na temat otwarcia się Microsoftu na środowisko open source można przeczytać na https://www.spidersweb.pl/2016/11/linux-foundation-microsoft.html. W każdym razie dzisiejszy wpis będzie o moich przygodach z skonfigurowaniem Dockera ( https://www.docker.com/ ) pod bazę Microsoft SQL Server dla jednego z moich projektów. By to zrobić użyje jeszcze docker-compose, tak aby móc wygodnie sterować wszystkimi wymaganymi kontenerami przypisanymi do projektu. Natomiast w celu utworzenia przykładowych baz danych i użytkowników trzeba będzie przebudować obraz sql servera.

Plik docker-compose.yml może wyglądać tak

version: '2.0'

services:
    mssql:
        build:
            context: .
            dockerfile: docker/Dockerfile-mssql
        image: mymssql
        networks:
            protectedNetwork:
                ipv4_address: 192.168.56.5

networks:
  protectedNetwork:
    ipam:
     config:
       - subnet: 192.168.56.0/24
         gateway: 192.168.56.99 # disable traffic

Wszystkie artefakty konieczne do budowy obrazu będę trzymał w katalogu docker/. W projekcie potrzebna jest wewnętrzna sieć (tj. protectedNetwork) do współdzielenia zasobów między kontenerami.

Plik docker/Dockerfile-mssql

FROM microsoft/mssql-server-linux

RUN mkdir -p /opt/mssql-scripts
ADD docker/mssql.sql /opt/mssql-scripts

ENV SA_PASSWORD tajneHaslo
ENV ACCEPT_EULA Y

RUN ( /opt/mssql/bin/sqlservr --accept-eula & ) | grep -q "Service Broker manager has started" \
    && /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'tajneHaslo' -i /opt/mssql-scripts/mssql.sql \
    && pkill sqlservr 

W tym miejscu instruujemy Dockera by pobrał utworzony przez Microsoft gotowy obraz SQL Servera, utworzył katalog i skopiował do niego plik z instrukcjami sql (mssql.sql, zawartość podana niżej). By obraz był gotowy należy jeszcze utworzyć zmienne środowiskowe z hasłem i akceptacją umowy (SA_PASSWORD, ACCEPT_EULA). Ostatnia linia wymusza uruchomienie Sql Servera ( w trakcie budowy obrazu), aby załadować plik sql.

Plik docker/mssql.sql

CREATE DATABASE databasemock;
GO
CREATE LOGIN databasemockLogin WITH PASSWORD = 'databaseMock1#';
USE databasemock;
CREATE USER databasemockUser FOR LOGIN databasemockLogin WITH DEFAULT_SCHEMA = databasemock;
GO
GRANT ALL TO databasemockUser;
GO

Plik z instrukcjami SQL zawiera jedynie utworzenie bazy danych i użytkownika z uprawnieniami do niej.

Po zbudowaniu obrazu (docker-compose build) i uruchomienia kontenerów (docker-compose up), w celu podłączenia się do bazy danych można użyć narzędzia sqlcmd. Również jest on dostępny dla Linuxa ;-) https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools?view=sql-server-2017.

W uproszczeniu /opt/mssql-tools/bin/sqlcmd -S Adres IP,1433 -U Użytkownik -h -1 -P 'hasło' -d NazwaBazayDanych

Parametr -h -1 jest opcjonalny, wyłącza wyświetlanie nagłówów tj. na ekranie będą prezentowane jedynie wyniki zapytań bez dodatkowego ich formatowania.

Polecenia SQL można wysłać z pliku (jako batch) po przez parametr -i nazwapliku, albo wykonać z terminala np. jako skrypt, parametr -Q "Zapytanie SQL"

Właściwie to tyle na dziś. Nie wiem jak Wy, ale ja jestem pod wrażeniem obecną polityką Microsoftu. Oby tak dalej!

Kiedy CQRS a kiedy CRUD ?

Dzisiaj trochę o architekturze systemów informatycznych. Wzorzec Command Query Responsibility Segregation (CQRS) a dobrze już poznany model Create Read Update and Delete (CRUD). Na początek krótka charakterystyka, a w dalszej części spróbuje opisać w jakich przypadkach Fowler uważa CQRS za lepsze rozwiązanie.

CQRS pozwala nam potraktować dane w odmienny sposób niż w tradycyjnym ujęciu CRUD. Dla przykładu składanie modelu z wielu rekordów czy tworzenie jakiegoś wirtualnego rekordu łączącego różne miejsca. W przypadku aktualizacji danych może to być określenie reguł sprawdzenia poprawności tych danych czy też zezwolenie na przechowywanie tylko pewnych ich kombinacji. Widać tutaj, że użytkownik tak naprawdę korzysta z wielu różnych od siebie reprezentacji tych informacji.

Problem w tym, że w takim utartym już podejściu CRUD, gdy istnieje potrzeba rozdzielenia i na nowo zdefiniowania nowego modelu danych to siłą rzeczy sprowadzamy ją do jednego miejsca, która działa jako punkt integracji tych wszystkich koncepcji. Natomiast CQRS rozdziela ten model na modele aktualizacji (Command) i wyświetlania (Query). Fowler jako uzasadnienie tego podziału wskazuje, że w przypadku bardziej skomplikowanych aplikacji, posiadanie wspólnego modelu (dla poleceń i zapytań), czyni kod zbyt złożonym.

Zatem wracając do pytania od którego zacząłem kiedy CQRS a kiedy CRUD ? Zdaniem Fowlera nie powinno się używać CQRS w całej aplikacji, a jedynie w określonych jej częściach (np. BoundedContext w języku DDD). Co więcej owa część (domena) musi dać się zamodelować w taki sposób, inaczej niepotrzebnie zwiększymy złożoność kodu. Zaletą zastosowania CQRS jest też poprawa wydajności aplikacji. Dzieje się tak poprzez podział obowiązków, obie części programu można skalować w sposób od siebie niezależny. Jak również wprowadzając różne strategie optymalizacji czy techniki dostępu do bazy danych.

Reasumując CQRS jest wzorcem, o którym warto sobie przypomnieć, gdy tradycyjny model CRUD zawodzi. Trzeba jednak mieć na uwadze, że jego implementacja nie należy do łatwych. Stosując go jedynie w takich częściach systemu, gdzie wspomniany podział wydaje się być naturalny.

Źródło: https://martinfowler.com/bliki/CQRS.html

set_error_handler jako elegancki sposób na bolączki z PHP

Gdy PHP powstawało nikt nawet nie śnił, że to narzędzie znajdzie zastosowanie w dużych projektach informatycznych aniżeli tylko w prostych skryptach. Pewnym tego pokłosiem jest wprowadzenie mechanizmu wyjątków (https://www.php.net/manual/en/language.exceptions.php) dopiero w wersji 5. Niestety ze względu na kompatybilność sporo wbudowanych funkcji, struktur języka takich jak np. fopen(), mail(), dzielenie przez zero etc. nie zgłasza wyjątków a E_ERROR, E_WARNING.. (https://php.net/manual/en/errorfunc.constants.php).

Jeśli zamiast tego chcielibyśmy otrzymać wyjątek lub inaczej obsłużyć taką sytuacje to trzeba customizować obsługę błędów w PHP właśnie przez set_error_handler(). Poniżej przykład zaczerpnięty z Zend Mail (zendframework/zend-mail/src/Transport/Sendmail.php)

/**
 * Send mail using PHP native mail()
 *
 * @param  string $to
 * @param  string $subject
 * @param  string $message
 * @param  string $headers
 * @param  $parameters
 * @throws \Zend\Mail\Transport\Exception\RuntimeException
 */
public function mailHandler($to, $subject, $message, $headers, $parameters)
{
    set_error_handler([$this, 'handleMailErrors']);
    if ($parameters === null) {
        $result = mail($to, $subject, $message, $headers);
    } else {
        $result = mail($to, $subject, $message, $headers, $parameters);
    }
    restore_error_handler();

    if ($this->errstr !== null || ! $result) {
        $errstr = $this->errstr;
        if (empty($errstr)) {
            $errstr = 'Unknown error';
        }
        throw new Exception\RuntimeException('Unable to send mail: ' . $errstr);
    }
}

Z tego miejsca warto przypomnieć, że gdy już się customizuje error handler to należy zadbać o zapisanie kompletnej informacji o pochodzeniu błędu (komunikat, miejsce wywołania i inne dodatkowe informacje ułatwiające debugowanie).

Więcej w https://www.php.net/manual/en/function.set-error-handler.php