SlideShare une entreprise Scribd logo
1  sur  52
Télécharger pour lire hors ligne
Automatyzacja utrzymania
jakości w środowisku PHP
Krzysztof Rewak
CTO w Blumilk
krzysztof.rewak@blumilk.pl
ECS
Kilka przemyśleń
● każdy z nas ma swoje nawyki przy programowaniu
● pracując w zespole wypadałoby ujednolicić styl
● różne style koniec końców doprowadzą do konfliktów
● wszyscy znamy wojenki taby kontra spacje, prawda?
Kilka faktów
● PHP-FIG opracowuje PHP Standards Recommendations
● obecny standard stylu to PSR-12 określany jako
Extended Coding Style Guide
● dzięki niemu możemy ujednolicić pewne konwencje
Spójrzmy na ten bajzel:
<?php
namespace BrewmapCollections Builders;
use BrewmapModelsCountry;
use BrewmapCollections Countries as CountriesCollection ;
final class CountriesBuilder
{
static function buildFromJson (string $jsonFile) {
$countries = new CountriesCollection ();
$data = json_decode($jsonFile, true);
foreach($data['countries' ] as $countryData )
{
$country = null;
$countries->addCountry(new Country($countryData ["name"], $countryData ["symbol"]));
}
return $countries;
}
}
Wstępne code review:
<?php // gdzie jest declare(strict_types=1) i wolne linie?
namespace BrewmapCollections Builders;
use BrewmapModelsCountry; // dlaczego importy nie są alfabetycznie?
use BrewmapCollections Countries as CountriesCollection ;
final class CountriesBuilder // może Countries byłoby wystarczającą nazwą?
{ // gdzie modyfikator dostępu metody poniżej?
static function buildFromJson (string $jsonFile) { // co z tą klamrą? gdzie return type?
$countries = new CountriesCollection (); // czy to taby?
$data = json_decode($jsonFile, true); // przydałby się jakiś pusty wiersz
foreach($data['countries' ] as $countryData ) // apostrofy czy cudzysłowy?
{
$country = null; // co to za zmienna?
$countries->addCountry(new Country($countryData ["name"], $countryData ["symbol"]));
}
return $countries;
}
} // gdzie pusta linia na końcu pliku?
Lepiej?
<?php
declare(strict_types=1);
namespace BrewmapCollectionsBuilders;
use BrewmapCollectionsCountries as CountriesCollection;
use BrewmapModelsCountry;
final class Countries
{
public static function buildFromJson(string $jsonFile): CountriesCollection
{
$countries = new CountriesCollection();
$data = json_decode($jsonFile, true);
foreach ($data["countries"] as $countryData) {
$countries->addCountry(new Country($countryData["name"], $countryData["symbol"]));
}
return $countries;
}
}
Jak to poprawić?
● ręcznie (och)
Jak to poprawić?
● ręcznie (och)
● korzystając z presetów w IDE i ulubionej kombinacji
klawiszowej: Ctrl+Alt+L
Jak to poprawić?
● ręcznie (och)
● korzystając z presetów w IDE i ulubionej kombinacji
klawiszowej: Ctrl+Alt+L
● zautomatyzować to!
Co nam daje Github?
Co nam daje Github?
Prosta instalacja
composer require symplify/easy-coding-standard--dev
Prosta instalacja
composer require symplify/easy-coding-standard --dev
Using version ^8.3 for symplify/easy-coding-standard
Running composer update symplify/easy-coding-standard
Loading composer repositories with package information
Updating dependencies
Lock file operations: 61 installs, 0 updates, 0 removals
- Locking composer/package-versions-deprecated (1.11.99)
- Locking composer/semver (3.2.2)
- Locking composer/xdebug-handler (1.4.4)
- Locking dealerdirect/phpcodesniffer-composer-installer (v0.7.0)
- Locking doctrine/annotations (1.11.1)
- Locking doctrine/lexer (1.2.1)
- Locking friendsofphp/php-cs-fixer (v2.16.7)
- Locking jean85/pretty-package-versions (1.5.1)
- Locking nette/finder (v2.5.2)
- Locking nette/robot-loader (v3.3.1)
- Locking nette/utils (v3.1.3)
- Locking nikic/php-parser (v4.10.2)
- Locking php-cs-fixer/diff (v1.3.1)
- Locking phpstan/phpdoc-parser (0.4.9)
- Locking phpstan/phpstan (0.12.52)
- Locking psr/cache (1.0.1)
- Locking psr/container (1.0.0)
- Locking psr/event-dispatcher (1.0.0)
Proste uruchomienie… a jednak nie?
./vendor/bin/ecs check
In AbstractCheckCommand.php line 123:
No checkers were found. Register them in your config in "services:" section, load them via "--config <file>.yml" or
"--set <set>" option.
check [--fix] [--clear-cache] [--no-progress-bar] [--no-error-table] [--output-format OUTPUT-FORMAT] [--]
[<source>...]
Jak to uruchomić?
Będziemy musieli utworzyć plik ecs.php w głównym
katalogu projektu. Można pobrać gotowy szablon z
repozytorium projektu.
W poprzednich wersjach był to YAML, dlatego warto
popatrzeć czy mamy ECS-a nowszego niż 6.0.0.
ecs.php z oficjalnego źródła
<?php
declare(strict_types=1);
use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;
use SymplifyEasyCodingStandardValueObjectOption;
use SymplifyEasyCodingStandardValueObjectSetSetList;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/config', __DIR__ . '/ecs.php']);
$parameters->set(
Option::SETS,
[
SetList::COMMON,
SetList::CLEAN_CODE,
SetList::DEAD_CODE,
SetList::PSR_12,
SetList::PHP_70,
SetList::PHP_71,
]
);
};
Dygresja konfiguracyjna
Swego czasu stworzyłem własny plik konfiguracyjny,
który kopiuję między różnymi projektami.
Przypuszczam, że przy większości projektów ecs.php
będzie z czasem mocno ewoluował. Nie bójcie się go
dostosować do swoich własnych potrzeb!
ecs.php
<?php
declare(strict_types=1);
use KrzysztofRewakPhpCsFixerDoubleQuoteFixerDoubleQuoteFixer;
use PhpCsFixerFixerCastNotationCastSpacesFixer;
use PhpCsFixerFixerClassNotationClassAttributesSeparationFixer;
use PhpCsFixerFixerOperatorNotOperatorWithSuccessorSpaceFixer;
use PhpCsFixerFixerPhpdocPhpdocLineSpanFixer;
use PhpCsFixerFixerStrictDeclareStrictTypesFixer;
use PhpCsFixerFixerStringNotationSingleQuoteFixer;
use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;
use SymplifyEasyCodingStandardValueObjectOption;
use SymplifyEasyCodingStandardValueObjectSetSetList;
$sets = [
SetList::CLEAN_CODE,
SetList::PSR_12,
SetList::PHP_71,
SetList::COMMON,
];
// dalsza część na drugiej stronie
ecs.php
$skipped = [
SingleQuoteFixer::class => null,
ClassAttributesSeparationFixer::class => null,
NotOperatorWithSuccessorSpaceFixer::class => null,
];
$rules = [
DeclareStrictTypesFixer::class => null,
CastSpacesFixer::class => ["space" => "none"],
DoubleQuoteFixer::class => null,
];
return static function (ContainerConfigurator $containerConfigurator) use ($sets, $skipped, $rules): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, $sets);
$parameters->set(Option::SKIP, $skipped);
$parameters->set(Option::PATHS, ["app", "config", "database", "resources/lang", "routes"]);
$services = $containerConfigurator->services();
foreach ($rules as $rule => $configuration) {
$service = $services->set($rule);
if ($configuration) {
$service->call("configure", [$configuration]);
}
}
};
Proste uruchomienie
./vendor/bin/ecs check
0/75 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
15/75 [▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░] 20%
30/75 [▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░] 40%
45/75 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░] 60%
60/75 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░] 80%
[OK] No errors found. Great job - your code is shiny in style!
75/75 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
Prosty przykład
Wróćmy zatem do przykładu z początku prezentacji.
Dodałem załączony kod do katalogu podpiętego pod ECS.
Po uruchomieniu programu otrzymałem całe mnóstwo
informacji na temat mojego stylu kodowania:
● tzw. diff
● listę zastosowanych checkerów
● dodatkowe informacje
Tzw. diff
--- Original
+++ New
@@ -1,16 +1,19 @@
<?php
+
+declare(strict_types=1);
+
namespace BrewmapCollectionsBuilders;
+use BrewmapCollectionsCountries as CountriesCollection;
use BrewmapModelsCountry;
-use BrewmapCollectionsCountries as CountriesCollection;
-final class CountriesBuilder
+final class Countries
{
- static function buildFromJson(string $jsonFile) {
+ public static function buildFromJson(string $jsonFile)
+ {
$countries = new CountriesCollection();
$data = json_decode($jsonFile, true);
- foreach($data['countries'] as $countryData)
- {
+ foreach ($data["countries"] as $countryData) {
$country = null;
$countries->addCountry(new Country($countryData["name"], $countryData["symbol"]));
}
Zastosowane checkery
55/55 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
Applied checkers:
* KrzysztofRewakPhpCsFixerDoubleQuoteFixerDoubleQuoteFixer
* PhpCsFixerFixerBasicBracesFixer
* PhpCsFixerFixerBasicPsr4Fixer
* PhpCsFixerFixerClassNotationVisibilityRequiredFixer
* PhpCsFixerFixerImportOrderedImportsFixer
* PhpCsFixerFixerNamespaceNotationSingleBlankLineBeforeNamespaceFixer
* PhpCsFixerFixerPhpTagBlankLineAfterOpeningTagFixer
* PhpCsFixerFixerStrictDeclareStrictTypesFixer
Inne komunikaty
---------------------------------------------------------------------------------------------------------------------
backend/Collections/Builders/Countries.php:14
----------------------------------------------------------------------------------------------------------------------
Unused variable $country.
Reported by: "SlevomatCodingStandardSniffsVariablesUnusedVariableSniff.UnusedVariable"
----------------------------------------------------------------------------------------------------------------------
[ERROR] Found 1 error that needs to be fixed manually.
[WARNING] Good news is that 1 error is fixable! Just add "--fix" to console command and rerun to apply.
Fix!
Znalezienie tych wszystkich błędów to wspaniała rzecz.
Jeszcze wspanialszą jest to, że ECS pod spodem korzysta
z wszystkich funkcjonalności pozostałych narzędzi.
Spróbujmy dodać flagę --fix do polecenia.
Magia!
ECS poprawił wszystkie znalezione błędy oprócz
nieszczęsnej zadeklarowanej, ale nigdzie nie używanej
zmiennej. Tę jedną zmianę trzeba zrobić ręcznie.
Dobra rada
Jeżeli dodajecie ECS do istniejącego projektu, szczególnie
sporych rozmiarów, warto uruchamiać --fix na osobnym
branchu, żeby przez przypadek nic nie popsuć, a
jednocześnie przy commitowaniu sprawdzić wszystkie
zmiany.
PSALM
Kilka przemyśleń
● styl stylem, ale każdemu programiście zdarza się
zrobić nieprzemyślany błąd
● błąd może być “głupi”, na przykład if do którego nie da
się nigdy wejść
● ale czasami może to być coś bardziej
skomplikowanego i na pierwszy rzut oka
niewykrywalnego jak korzystanie z pól
niezadeklarowanych w konstruktorze
Co nam daje Github?
Prosta instalacja
composer require vimeo/psalm --dev
Prosta instalacja
composer require vimeo/psalm --dev
Using version ^4.1 for vimeo/psalm
./composer.json has been updated
Running composer update vimeo/psalm
Loading composer repositories with package information
Updating dependencies
Lock file operations: 13 installs, 0 updates, 0 removals
- Locking amphp/amp (v2.5.0)
- Locking amphp/byte-stream (v1.8.0)
- Locking dnoegel/php-xdg-base-dir (v0.1.1)
- Locking felixfbecker/advanced-json-rpc (v3.1.1)
- Locking felixfbecker/language-server-protocol (v1.5.0)
- Locking netresearch/jsonmapper (v2.1.0)
- Locking openlss/lib-array2xml (1.0.0)
- Locking phpdocumentor/reflection-common (2.2.0)
- Locking phpdocumentor/reflection-docblock (5.2.2)
- Locking phpdocumentor/type-resolver (1.4.0)
- Locking vimeo/psalm (4.1.0)
- Locking webmozart/assert (1.9.1)
- Locking webmozart/path-util (2.3.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 13 installs, 0 updates, 0 removals
- Downloading webmozart/assert (1.9.1)
Proste uruchomienie… a jednak nie?
./vendor/bin/psalm
Could not locate a config XML file in path /application/. Have you run 'psalm --init' ?
./vendor/bin/psalm --init
Calculating best config level based on project files
Scanning files...
Analyzing files...
E░
Detected level 2 as a suitable initial default
Config file created successfully. Please re-run psalm.
Kilka przemyśleń
● Psalm znajduje mnóstwo rzeczy, ale niestety nie na
wszystkie mamy wpływ
● warto zastanowić się co naprawdę jest istotne i te
mniej ważne sprawy ignorować przez pole
issueHandlers w pliku konfiguracyjnym psalm.xml
Problem z laravelowymi Commandami?
./vendor/bin/psalm
Scanning files...
Analyzing files...
E░░░░░░░░░░I░░I░░░░░░░░░░░░░░░░░░░░░░░II░░░I░░░░░░░░░░░░░░░░ 60 / 78 (76%)
░░░░░░░░░░░░░░░░░░
ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property
BrewmapConsoleCommandsImportGoogleMap::$laravel is not defined in constructor of
BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074)
class ImportGoogleMap extends Command
ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property
BrewmapConsoleCommandsImportGoogleMap::$name is not defined in constructor of
BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074)
class ImportGoogleMap extends Command
ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property
BrewmapConsoleCommandsImportGoogleMap::$input is not defined in constructor of
BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074)
class ImportGoogleMap extends Command
ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property
BrewmapConsoleCommandsImportGoogleMap::$output is not defined in constructor of
BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074)
class ImportGoogleMap extends Command
Problem z laravelowymi Commandami?
<issueHandlers >
<PropertyNotSetInConstructor >
<errorLevel type="suppress" >
< directory name="app/Console/Commands" />
< directory name="app/Nova/Metrics" />
< directory name="app/Http/Requests/" />
< directory name="app/Nova/Tools" />
< directory name="database/factories" />
</errorLevel>
</PropertyNotSetInConstructor >
</issueHandlers >
Kilka przykładów
● no to siup, obejrzyjmy kilka przykładów:
Głupie ify
if (false) {
die();
}
INFO: TypeDoesNotContainType - app/Console/Commands/ImportGoogleMap.php:30:12
if (false) is impossible (see https://psalm.dev/056 )
if(false) {
Nieużywany kod
class Localize
{
protected array $locales = ["en", "pl"];
protected Application $application;
protected string $legacy = "";
public function __construct(Application $application)
{
$this->application = $application;
}
ERROR: PossiblyUnusedProperty - app/Http/Middleware/Localize.php:16:22
Cannot find any references to property BrewmapHttpMiddlewareLocalize::$legacy (see
https://psalm.dev/149 )
protected string $legacy = "";
Głupotki tablicowe
protected function validateCheckSum(): void
{
$sum = 0;
for ($i = 0; $i < 10; $i++) {
$sum += self::CHECKSUM_WEIGHTS[$i] * $this->value[$i];
}
$checkSum = 10 - $sum % 10;
$checkNumber = $checkSum == 10 ? 0 : $checkSum;
ERROR: InvalidOperand - app/Providers/Validators/Pesel.php:72:50
Cannot perform a numeric operation with a non-numeric type string (see https://psalm.dev/058 )
$sum += self::CHECKSUM_WEIGHTS[$i] * $this->value[$i] ;
Niewłaściwe argumenty
public function render($request, Exception $exception): Response
{
$status = $exception->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR;
// (...)
$response = response()->json($body, $status, [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$this->logResponse($response);
return $response;
}
ERROR: InvalidScalarArgument - app/Exceptions/Handler.php:95:4 5
Argument 2 of IlluminateContractsRoutingResponseFactory::json expects int, int|string
provided (see https://psalm.dev/012 )
$response = response()->json($body, $status, [], JSON_UNESCAPED_UNICODE |
JSON_UNESCAPED_SLASHES);
Źle zwracane typy
use IlluminateHttpRedirectResponse ;
// (...)
public function redirectToFacebook (): RedirectResponse
{
return Socialite::driver("facebook")->redirect();
}
INFO: LessSpecificReturnStatement -
app/Http/Controllers/API/AuthenticationController.php:36:16
The type 'SymfonyComponentHttpFoundationRedirectResponse' is more general than the declared
return type 'IlluminateHttpRedirectResponse' for
BrewmapHttpControllersAPIAuthenticationController::redirectToFacebook (see
https://psalm.dev/129 )
return Socialite::driver("facebook")->redirect() ;
Źle zwracane typy
// GuzzleHttp/Client::_call() returns PromisePromiseInterface
class MockGuzzleClient extends Client
{
public function __call($method, $args): ResponseInterface
{
return new Response();
}
}
ERROR: ImplementedReturnTypeMismatch - app/Manager/MockGuzzleClient.php:20:16
The inherited return type 'GuzzleHttpPromisePromiseInterface' for GuzzleHttpClient::__call
is different to the implemented return type for CalclyCoreManagerMockGuzzleClient::__call
'PsrHttpMessageResponseInterface' (see https://psalm.dev/123 )
* @return ResponseInterface
Źle nazwane parametry metod
class Kernel extends ConsoleKernel
{
protected function commands(): void
{
$this->load(__DIR__ . "/Commands");
}
protected function renderException($output, Throwable $exception)
{
parent::renderException($output, $extension);
}
}
INFO: ParamNameMismatch - app/Console/Kernel.php:17:59
Argument 2 of BrewmapConsoleKernel::renderException has wrong name $exception, expecting $e
as defined by IlluminateFoundationConsoleKernel::renderException (see
https://psalm.dev/230 )
protected function renderException($output, Throwable $exception)
Wbudowana integracja z PHPStormem!
INTEGRACJA Z
PROJEKTEM
composer.json
{
"scripts": {
"post-autoload-dump" : [
"Illuminate FoundationComposerScripts::postAutoloadDump" ,
"@php artisan package:discover --ansi"
],
"psalm": "./vendor/bin/psalm" ,
"behat": "./vendor/bin/behat --format=progress" ,
"ecs": "./vendor/bin/ecs check" ,
"check": [
"composer psalm" ,
"composer behat" ,
"composer ecs"
]
}
}
Może git hooki?
A może Github Workflow?
name: Behaviour, code and style test
on:
push:
branches: [ "develop" ]
pull_request :
branches: [ "develop" ]
jobs:
build:
# (...)
- name: Run test suite
run: docker-compose run php composer behat
- name: Run code style checker
run: docker-compose run php composer ecs
- name: Run code static analysis
run: docker-compose run php composer psalm
Pytania?
krzysztof.rewak@blumilk.pl
@krzysztofrewak

Contenu connexe

Tendances

Laravel Octane - czy na pewno taki szybki?
Laravel Octane - czy na pewno taki szybki?Laravel Octane - czy na pewno taki szybki?
Laravel Octane - czy na pewno taki szybki?Laravel Poland MeetUp
 
Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...
Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...
Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...Droptica
 
Migrate API w Drupalu [PL]
Migrate API w Drupalu [PL]Migrate API w Drupalu [PL]
Migrate API w Drupalu [PL]Droptica
 
Metaprogramowanie w JS
Metaprogramowanie w JSMetaprogramowanie w JS
Metaprogramowanie w JSDawid Rusnak
 
PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...
PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...
PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...PROIDEA
 
Debugowanie skryptow php za pomoca xdebug
Debugowanie skryptow php za pomoca xdebugDebugowanie skryptow php za pomoca xdebug
Debugowanie skryptow php za pomoca xdebugXSolve
 
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...The Software House
 
Deployment kodu z Capistrano
Deployment kodu z CapistranoDeployment kodu z Capistrano
Deployment kodu z CapistranoMichał Szajbe
 
Laravel workshops 1
Laravel workshops 1Laravel workshops 1
Laravel workshops 1Kamil Fojuth
 
Laravel Dusk - prosty przepis na testy E2E
Laravel Dusk - prosty przepis na testy E2ELaravel Dusk - prosty przepis na testy E2E
Laravel Dusk - prosty przepis na testy E2ELaravel Poland MeetUp
 
Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]
Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]
Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]Droptica
 
Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"
Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"
Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"HighSolutions Sp. z o.o.
 
Jak stworzyliśmy system kudosów w Laravelu i Slacku
Jak stworzyliśmy system kudosów w Laravelu i SlackuJak stworzyliśmy system kudosów w Laravelu i Slacku
Jak stworzyliśmy system kudosów w Laravelu i SlackuLaravel Poland MeetUp
 
Drupal jako modularny i rozszerzalny CMS [PL]
Drupal jako modularny i rozszerzalny CMS [PL]Drupal jako modularny i rozszerzalny CMS [PL]
Drupal jako modularny i rozszerzalny CMS [PL]Droptica
 
Apache http server - proste i zaawansowane przypadki użycia
Apache http server - proste i zaawansowane przypadki użyciaApache http server - proste i zaawansowane przypadki użycia
Apache http server - proste i zaawansowane przypadki użyciaWojciech Lichota
 
Jak zostać mobile deweloperem w 1 dzień
Jak zostać mobile deweloperem w 1 dzieńJak zostać mobile deweloperem w 1 dzień
Jak zostać mobile deweloperem w 1 dzieńPaweł Kondraciuk
 

Tendances (20)

Laravel Octane - czy na pewno taki szybki?
Laravel Octane - czy na pewno taki szybki?Laravel Octane - czy na pewno taki szybki?
Laravel Octane - czy na pewno taki szybki?
 
Swoole w PHP. Czy to ma sens?
Swoole w PHP. Czy to ma sens?Swoole w PHP. Czy to ma sens?
Swoole w PHP. Czy to ma sens?
 
Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...
Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...
Droopler: instalacja z użyciem composer i przykład budowy prostej strony firm...
 
Migrate API w Drupalu [PL]
Migrate API w Drupalu [PL]Migrate API w Drupalu [PL]
Migrate API w Drupalu [PL]
 
Websockety w PHP
Websockety w PHPWebsockety w PHP
Websockety w PHP
 
Metaprogramowanie w JS
Metaprogramowanie w JSMetaprogramowanie w JS
Metaprogramowanie w JS
 
PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...
PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...
PLNOG22 - Piotr Stolarek - Bezpieczeństwo użytkowania platform usługowych Tel...
 
Debugowanie skryptow php za pomoca xdebug
Debugowanie skryptow php za pomoca xdebugDebugowanie skryptow php za pomoca xdebug
Debugowanie skryptow php za pomoca xdebug
 
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
 
Deployment kodu z Capistrano
Deployment kodu z CapistranoDeployment kodu z Capistrano
Deployment kodu z Capistrano
 
Laravel workshops 1
Laravel workshops 1Laravel workshops 1
Laravel workshops 1
 
Laravel czy Lumen, oto jest pytanie
Laravel czy Lumen, oto jest pytanieLaravel czy Lumen, oto jest pytanie
Laravel czy Lumen, oto jest pytanie
 
Laravel Dusk - prosty przepis na testy E2E
Laravel Dusk - prosty przepis na testy E2ELaravel Dusk - prosty przepis na testy E2E
Laravel Dusk - prosty przepis na testy E2E
 
Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]
Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]
Uwierzytelnianie dwuetapowe (2FA) w Drupalu [PL]
 
CruiseControl.rb
CruiseControl.rbCruiseControl.rb
CruiseControl.rb
 
Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"
Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"
Laravel Poznań Meetup #12 - "Laravel 6.0 - co nowego?"
 
Jak stworzyliśmy system kudosów w Laravelu i Slacku
Jak stworzyliśmy system kudosów w Laravelu i SlackuJak stworzyliśmy system kudosów w Laravelu i Slacku
Jak stworzyliśmy system kudosów w Laravelu i Slacku
 
Drupal jako modularny i rozszerzalny CMS [PL]
Drupal jako modularny i rozszerzalny CMS [PL]Drupal jako modularny i rozszerzalny CMS [PL]
Drupal jako modularny i rozszerzalny CMS [PL]
 
Apache http server - proste i zaawansowane przypadki użycia
Apache http server - proste i zaawansowane przypadki użyciaApache http server - proste i zaawansowane przypadki użycia
Apache http server - proste i zaawansowane przypadki użycia
 
Jak zostać mobile deweloperem w 1 dzień
Jak zostać mobile deweloperem w 1 dzieńJak zostać mobile deweloperem w 1 dzień
Jak zostać mobile deweloperem w 1 dzień
 

Similaire à Automatyzacja utrzymania jakości w środowisku PHP

WordUp Trójmiasto - Sage 9 w praktyce
WordUp Trójmiasto - Sage 9 w praktyceWordUp Trójmiasto - Sage 9 w praktyce
WordUp Trójmiasto - Sage 9 w praktyceDawid Urbański
 
Ganymede - nowoczesne technologie w grach przeglądarkowych i mobilnych
Ganymede - nowoczesne technologie w grach przeglądarkowych i mobilnychGanymede - nowoczesne technologie w grach przeglądarkowych i mobilnych
Ganymede - nowoczesne technologie w grach przeglądarkowych i mobilnychSKN Shader
 
Angular 4 pragmatycznie
Angular 4 pragmatycznieAngular 4 pragmatycznie
Angular 4 pragmatycznieSages
 
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Codesushi.co (CODESUSHI LLC)
 
Poznaj wp-config.php "Ukryte" możliwości.
Poznaj wp-config.php  "Ukryte" możliwości.Poznaj wp-config.php  "Ukryte" możliwości.
Poznaj wp-config.php "Ukryte" możliwości.Marcin Jóźwiak
 
Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Tomasz Dziuda
 
Zastosowanie buildout przy wdrażaniu projektów opartych o framework Django
Zastosowanie buildout przy wdrażaniu projektów opartych o framework DjangoZastosowanie buildout przy wdrażaniu projektów opartych o framework Django
Zastosowanie buildout przy wdrażaniu projektów opartych o framework DjangoDominik Szopa
 
Michał Dec - Quality in Clouds
Michał Dec - Quality in CloudsMichał Dec - Quality in Clouds
Michał Dec - Quality in Cloudskraqa
 
PLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOps
PLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOpsPLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOps
PLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOpsPROIDEA
 
Skalowanie PostgreSQL @ DBConf.PL 2014
Skalowanie PostgreSQL @ DBConf.PL 2014Skalowanie PostgreSQL @ DBConf.PL 2014
Skalowanie PostgreSQL @ DBConf.PL 2014Filip Rembialkowski
 
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) Laravel Poland MeetUp
 
Podstawy AngularJS
Podstawy AngularJSPodstawy AngularJS
Podstawy AngularJSSages
 
OSGi, deklaratywnie
OSGi, deklaratywnieOSGi, deklaratywnie
OSGi, deklaratywnieCode-House
 
Programowanie sterowników w Linuksie.
Programowanie sterowników w Linuksie.Programowanie sterowników w Linuksie.
Programowanie sterowników w Linuksie.Semihalf
 
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótkaWebpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótkaMarcin Gajda
 

Similaire à Automatyzacja utrzymania jakości w środowisku PHP (20)

WordUp Trójmiasto - Sage 9 w praktyce
WordUp Trójmiasto - Sage 9 w praktyceWordUp Trójmiasto - Sage 9 w praktyce
WordUp Trójmiasto - Sage 9 w praktyce
 
JavaScript, Moduły
JavaScript, ModułyJavaScript, Moduły
JavaScript, Moduły
 
Ganymede - nowoczesne technologie w grach przeglądarkowych i mobilnych
Ganymede - nowoczesne technologie w grach przeglądarkowych i mobilnychGanymede - nowoczesne technologie w grach przeglądarkowych i mobilnych
Ganymede - nowoczesne technologie w grach przeglądarkowych i mobilnych
 
Angular 4 pragmatycznie
Angular 4 pragmatycznieAngular 4 pragmatycznie
Angular 4 pragmatycznie
 
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
 
Poznaj wp-config.php "Ukryte" możliwości.
Poznaj wp-config.php  "Ukryte" możliwości.Poznaj wp-config.php  "Ukryte" możliwości.
Poznaj wp-config.php "Ukryte" możliwości.
 
Mongodb with Rails
Mongodb with RailsMongodb with Rails
Mongodb with Rails
 
Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014
 
Zastosowanie buildout przy wdrażaniu projektów opartych o framework Django
Zastosowanie buildout przy wdrażaniu projektów opartych o framework DjangoZastosowanie buildout przy wdrażaniu projektów opartych o framework Django
Zastosowanie buildout przy wdrażaniu projektów opartych o framework Django
 
Michał Dec - Quality in Clouds
Michał Dec - Quality in CloudsMichał Dec - Quality in Clouds
Michał Dec - Quality in Clouds
 
PLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOps
PLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOpsPLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOps
PLNOG 22 - Krzysztof Załęski - Praktyczne zastosowanie narzędzi NetDevOps
 
Podstawy PHP
Podstawy PHPPodstawy PHP
Podstawy PHP
 
Torquebox
TorqueboxTorquebox
Torquebox
 
Wprowadzenie do PHPUnit
Wprowadzenie do PHPUnitWprowadzenie do PHPUnit
Wprowadzenie do PHPUnit
 
Skalowanie PostgreSQL @ DBConf.PL 2014
Skalowanie PostgreSQL @ DBConf.PL 2014Skalowanie PostgreSQL @ DBConf.PL 2014
Skalowanie PostgreSQL @ DBConf.PL 2014
 
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
 
Podstawy AngularJS
Podstawy AngularJSPodstawy AngularJS
Podstawy AngularJS
 
OSGi, deklaratywnie
OSGi, deklaratywnieOSGi, deklaratywnie
OSGi, deklaratywnie
 
Programowanie sterowników w Linuksie.
Programowanie sterowników w Linuksie.Programowanie sterowników w Linuksie.
Programowanie sterowników w Linuksie.
 
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótkaWebpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
 

Plus de Laravel Poland MeetUp

WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...
WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...
WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...Laravel Poland MeetUp
 
Kilka slajdów o castowaniu atrybutów w Eloquent
Kilka slajdów o castowaniu atrybutów w EloquentKilka slajdów o castowaniu atrybutów w Eloquent
Kilka slajdów o castowaniu atrybutów w EloquentLaravel Poland MeetUp
 
Jak przyspieszyłem aplikację produkcyjną o ponad 40%
Jak przyspieszyłem aplikację produkcyjną o ponad 40%Jak przyspieszyłem aplikację produkcyjną o ponad 40%
Jak przyspieszyłem aplikację produkcyjną o ponad 40%Laravel Poland MeetUp
 
Enumy w Laravelu - dlaczego warto stosować?
Enumy w Laravelu - dlaczego warto stosować?Enumy w Laravelu - dlaczego warto stosować?
Enumy w Laravelu - dlaczego warto stosować?Laravel Poland MeetUp
 
Przegląd najciekawszych wtyczek do Laravela
Przegląd najciekawszych wtyczek do LaravelaPrzegląd najciekawszych wtyczek do Laravela
Przegląd najciekawszych wtyczek do LaravelaLaravel Poland MeetUp
 
Wstęp do Gitlab CI/CD w aplikacjach napisanych w Laravel
Wstęp do Gitlab CI/CD w aplikacjach napisanych w LaravelWstęp do Gitlab CI/CD w aplikacjach napisanych w Laravel
Wstęp do Gitlab CI/CD w aplikacjach napisanych w LaravelLaravel Poland MeetUp
 
Laravel Collection - tablice na sterydach
Laravel Collection - tablice na sterydachLaravel Collection - tablice na sterydach
Laravel Collection - tablice na sterydachLaravel Poland MeetUp
 
Speed up web API with Laravel and Swoole using Docker
Speed up web API with Laravel and Swoole using DockerSpeed up web API with Laravel and Swoole using Docker
Speed up web API with Laravel and Swoole using DockerLaravel Poland MeetUp
 
Przetwarzanie Asynchroniczne i Promises w Laravel
Przetwarzanie Asynchroniczne i Promises w LaravelPrzetwarzanie Asynchroniczne i Promises w Laravel
Przetwarzanie Asynchroniczne i Promises w LaravelLaravel Poland MeetUp
 

Plus de Laravel Poland MeetUp (20)

WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...
WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...
WebRTC+Websockety - Jak stworzyłem aplikację do kamerek internetowych w Larav...
 
Kilka slajdów o castowaniu atrybutów w Eloquent
Kilka slajdów o castowaniu atrybutów w EloquentKilka slajdów o castowaniu atrybutów w Eloquent
Kilka slajdów o castowaniu atrybutów w Eloquent
 
Licencje otwartego oprogramowania
Licencje otwartego oprogramowaniaLicencje otwartego oprogramowania
Licencje otwartego oprogramowania
 
Jak przyspieszyłem aplikację produkcyjną o ponad 40%
Jak przyspieszyłem aplikację produkcyjną o ponad 40%Jak przyspieszyłem aplikację produkcyjną o ponad 40%
Jak przyspieszyłem aplikację produkcyjną o ponad 40%
 
Jak przemycić Shape Up do Scruma?
Jak przemycić Shape Up do Scruma?Jak przemycić Shape Up do Scruma?
Jak przemycić Shape Up do Scruma?
 
Enumy w Laravelu - dlaczego warto stosować?
Enumy w Laravelu - dlaczego warto stosować?Enumy w Laravelu - dlaczego warto stosować?
Enumy w Laravelu - dlaczego warto stosować?
 
Przegląd najciekawszych wtyczek do Laravela
Przegląd najciekawszych wtyczek do LaravelaPrzegląd najciekawszych wtyczek do Laravela
Przegląd najciekawszych wtyczek do Laravela
 
Walidacja w Laravelu
Walidacja w LaraveluWalidacja w Laravelu
Walidacja w Laravelu
 
(prawie) Wszystko o Tinkerze
(prawie) Wszystko o Tinkerze(prawie) Wszystko o Tinkerze
(prawie) Wszystko o Tinkerze
 
Laravel Jobs i PHP8
Laravel Jobs i PHP8Laravel Jobs i PHP8
Laravel Jobs i PHP8
 
Laravel/PHP - zderzenie z PDFami
Laravel/PHP - zderzenie z PDFamiLaravel/PHP - zderzenie z PDFami
Laravel/PHP - zderzenie z PDFami
 
Action-based Laravel
Action-based LaravelAction-based Laravel
Action-based Laravel
 
Wstęp do Gitlab CI/CD w aplikacjach napisanych w Laravel
Wstęp do Gitlab CI/CD w aplikacjach napisanych w LaravelWstęp do Gitlab CI/CD w aplikacjach napisanych w Laravel
Wstęp do Gitlab CI/CD w aplikacjach napisanych w Laravel
 
Laravel Collection - tablice na sterydach
Laravel Collection - tablice na sterydachLaravel Collection - tablice na sterydach
Laravel Collection - tablice na sterydach
 
AOP w Laravel
AOP w LaravelAOP w Laravel
AOP w Laravel
 
Speed up web API with Laravel and Swoole using Docker
Speed up web API with Laravel and Swoole using DockerSpeed up web API with Laravel and Swoole using Docker
Speed up web API with Laravel and Swoole using Docker
 
Laravel 6.0 - co nowego?
Laravel 6.0 - co nowego?Laravel 6.0 - co nowego?
Laravel 6.0 - co nowego?
 
Przetwarzanie Asynchroniczne i Promises w Laravel
Przetwarzanie Asynchroniczne i Promises w LaravelPrzetwarzanie Asynchroniczne i Promises w Laravel
Przetwarzanie Asynchroniczne i Promises w Laravel
 
KPI w projektach IT
KPI w projektach ITKPI w projektach IT
KPI w projektach IT
 
Mikrousługi w allegro
Mikrousługi w allegroMikrousługi w allegro
Mikrousługi w allegro
 

Automatyzacja utrzymania jakości w środowisku PHP

  • 2. Krzysztof Rewak CTO w Blumilk krzysztof.rewak@blumilk.pl
  • 3. ECS
  • 4. Kilka przemyśleń ● każdy z nas ma swoje nawyki przy programowaniu ● pracując w zespole wypadałoby ujednolicić styl ● różne style koniec końców doprowadzą do konfliktów ● wszyscy znamy wojenki taby kontra spacje, prawda?
  • 5. Kilka faktów ● PHP-FIG opracowuje PHP Standards Recommendations ● obecny standard stylu to PSR-12 określany jako Extended Coding Style Guide ● dzięki niemu możemy ujednolicić pewne konwencje
  • 6. Spójrzmy na ten bajzel: <?php namespace BrewmapCollections Builders; use BrewmapModelsCountry; use BrewmapCollections Countries as CountriesCollection ; final class CountriesBuilder { static function buildFromJson (string $jsonFile) { $countries = new CountriesCollection (); $data = json_decode($jsonFile, true); foreach($data['countries' ] as $countryData ) { $country = null; $countries->addCountry(new Country($countryData ["name"], $countryData ["symbol"])); } return $countries; } }
  • 7. Wstępne code review: <?php // gdzie jest declare(strict_types=1) i wolne linie? namespace BrewmapCollections Builders; use BrewmapModelsCountry; // dlaczego importy nie są alfabetycznie? use BrewmapCollections Countries as CountriesCollection ; final class CountriesBuilder // może Countries byłoby wystarczającą nazwą? { // gdzie modyfikator dostępu metody poniżej? static function buildFromJson (string $jsonFile) { // co z tą klamrą? gdzie return type? $countries = new CountriesCollection (); // czy to taby? $data = json_decode($jsonFile, true); // przydałby się jakiś pusty wiersz foreach($data['countries' ] as $countryData ) // apostrofy czy cudzysłowy? { $country = null; // co to za zmienna? $countries->addCountry(new Country($countryData ["name"], $countryData ["symbol"])); } return $countries; } } // gdzie pusta linia na końcu pliku?
  • 8. Lepiej? <?php declare(strict_types=1); namespace BrewmapCollectionsBuilders; use BrewmapCollectionsCountries as CountriesCollection; use BrewmapModelsCountry; final class Countries { public static function buildFromJson(string $jsonFile): CountriesCollection { $countries = new CountriesCollection(); $data = json_decode($jsonFile, true); foreach ($data["countries"] as $countryData) { $countries->addCountry(new Country($countryData["name"], $countryData["symbol"])); } return $countries; } }
  • 9. Jak to poprawić? ● ręcznie (och)
  • 10. Jak to poprawić? ● ręcznie (och) ● korzystając z presetów w IDE i ulubionej kombinacji klawiszowej: Ctrl+Alt+L
  • 11. Jak to poprawić? ● ręcznie (och) ● korzystając z presetów w IDE i ulubionej kombinacji klawiszowej: Ctrl+Alt+L ● zautomatyzować to!
  • 12. Co nam daje Github?
  • 13. Co nam daje Github?
  • 14. Prosta instalacja composer require symplify/easy-coding-standard--dev
  • 15. Prosta instalacja composer require symplify/easy-coding-standard --dev Using version ^8.3 for symplify/easy-coding-standard Running composer update symplify/easy-coding-standard Loading composer repositories with package information Updating dependencies Lock file operations: 61 installs, 0 updates, 0 removals - Locking composer/package-versions-deprecated (1.11.99) - Locking composer/semver (3.2.2) - Locking composer/xdebug-handler (1.4.4) - Locking dealerdirect/phpcodesniffer-composer-installer (v0.7.0) - Locking doctrine/annotations (1.11.1) - Locking doctrine/lexer (1.2.1) - Locking friendsofphp/php-cs-fixer (v2.16.7) - Locking jean85/pretty-package-versions (1.5.1) - Locking nette/finder (v2.5.2) - Locking nette/robot-loader (v3.3.1) - Locking nette/utils (v3.1.3) - Locking nikic/php-parser (v4.10.2) - Locking php-cs-fixer/diff (v1.3.1) - Locking phpstan/phpdoc-parser (0.4.9) - Locking phpstan/phpstan (0.12.52) - Locking psr/cache (1.0.1) - Locking psr/container (1.0.0) - Locking psr/event-dispatcher (1.0.0)
  • 16. Proste uruchomienie… a jednak nie? ./vendor/bin/ecs check In AbstractCheckCommand.php line 123: No checkers were found. Register them in your config in "services:" section, load them via "--config <file>.yml" or "--set <set>" option. check [--fix] [--clear-cache] [--no-progress-bar] [--no-error-table] [--output-format OUTPUT-FORMAT] [--] [<source>...]
  • 17. Jak to uruchomić? Będziemy musieli utworzyć plik ecs.php w głównym katalogu projektu. Można pobrać gotowy szablon z repozytorium projektu. W poprzednich wersjach był to YAML, dlatego warto popatrzeć czy mamy ECS-a nowszego niż 6.0.0.
  • 18. ecs.php z oficjalnego źródła <?php declare(strict_types=1); use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator; use SymplifyEasyCodingStandardValueObjectOption; use SymplifyEasyCodingStandardValueObjectSetSetList; return static function (ContainerConfigurator $containerConfigurator): void { $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/config', __DIR__ . '/ecs.php']); $parameters->set( Option::SETS, [ SetList::COMMON, SetList::CLEAN_CODE, SetList::DEAD_CODE, SetList::PSR_12, SetList::PHP_70, SetList::PHP_71, ] ); };
  • 19. Dygresja konfiguracyjna Swego czasu stworzyłem własny plik konfiguracyjny, który kopiuję między różnymi projektami. Przypuszczam, że przy większości projektów ecs.php będzie z czasem mocno ewoluował. Nie bójcie się go dostosować do swoich własnych potrzeb!
  • 20. ecs.php <?php declare(strict_types=1); use KrzysztofRewakPhpCsFixerDoubleQuoteFixerDoubleQuoteFixer; use PhpCsFixerFixerCastNotationCastSpacesFixer; use PhpCsFixerFixerClassNotationClassAttributesSeparationFixer; use PhpCsFixerFixerOperatorNotOperatorWithSuccessorSpaceFixer; use PhpCsFixerFixerPhpdocPhpdocLineSpanFixer; use PhpCsFixerFixerStrictDeclareStrictTypesFixer; use PhpCsFixerFixerStringNotationSingleQuoteFixer; use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator; use SymplifyEasyCodingStandardValueObjectOption; use SymplifyEasyCodingStandardValueObjectSetSetList; $sets = [ SetList::CLEAN_CODE, SetList::PSR_12, SetList::PHP_71, SetList::COMMON, ]; // dalsza część na drugiej stronie
  • 21. ecs.php $skipped = [ SingleQuoteFixer::class => null, ClassAttributesSeparationFixer::class => null, NotOperatorWithSuccessorSpaceFixer::class => null, ]; $rules = [ DeclareStrictTypesFixer::class => null, CastSpacesFixer::class => ["space" => "none"], DoubleQuoteFixer::class => null, ]; return static function (ContainerConfigurator $containerConfigurator) use ($sets, $skipped, $rules): void { $parameters = $containerConfigurator->parameters(); $parameters->set(Option::SETS, $sets); $parameters->set(Option::SKIP, $skipped); $parameters->set(Option::PATHS, ["app", "config", "database", "resources/lang", "routes"]); $services = $containerConfigurator->services(); foreach ($rules as $rule => $configuration) { $service = $services->set($rule); if ($configuration) { $service->call("configure", [$configuration]); } } };
  • 22. Proste uruchomienie ./vendor/bin/ecs check 0/75 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% 15/75 [▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░] 20% 30/75 [▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░] 40% 45/75 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░] 60% 60/75 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░] 80% [OK] No errors found. Great job - your code is shiny in style! 75/75 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
  • 23. Prosty przykład Wróćmy zatem do przykładu z początku prezentacji. Dodałem załączony kod do katalogu podpiętego pod ECS. Po uruchomieniu programu otrzymałem całe mnóstwo informacji na temat mojego stylu kodowania: ● tzw. diff ● listę zastosowanych checkerów ● dodatkowe informacje
  • 24. Tzw. diff --- Original +++ New @@ -1,16 +1,19 @@ <?php + +declare(strict_types=1); + namespace BrewmapCollectionsBuilders; +use BrewmapCollectionsCountries as CountriesCollection; use BrewmapModelsCountry; -use BrewmapCollectionsCountries as CountriesCollection; -final class CountriesBuilder +final class Countries { - static function buildFromJson(string $jsonFile) { + public static function buildFromJson(string $jsonFile) + { $countries = new CountriesCollection(); $data = json_decode($jsonFile, true); - foreach($data['countries'] as $countryData) - { + foreach ($data["countries"] as $countryData) { $country = null; $countries->addCountry(new Country($countryData["name"], $countryData["symbol"])); }
  • 25. Zastosowane checkery 55/55 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% Applied checkers: * KrzysztofRewakPhpCsFixerDoubleQuoteFixerDoubleQuoteFixer * PhpCsFixerFixerBasicBracesFixer * PhpCsFixerFixerBasicPsr4Fixer * PhpCsFixerFixerClassNotationVisibilityRequiredFixer * PhpCsFixerFixerImportOrderedImportsFixer * PhpCsFixerFixerNamespaceNotationSingleBlankLineBeforeNamespaceFixer * PhpCsFixerFixerPhpTagBlankLineAfterOpeningTagFixer * PhpCsFixerFixerStrictDeclareStrictTypesFixer
  • 26. Inne komunikaty --------------------------------------------------------------------------------------------------------------------- backend/Collections/Builders/Countries.php:14 ---------------------------------------------------------------------------------------------------------------------- Unused variable $country. Reported by: "SlevomatCodingStandardSniffsVariablesUnusedVariableSniff.UnusedVariable" ---------------------------------------------------------------------------------------------------------------------- [ERROR] Found 1 error that needs to be fixed manually. [WARNING] Good news is that 1 error is fixable! Just add "--fix" to console command and rerun to apply.
  • 27. Fix! Znalezienie tych wszystkich błędów to wspaniała rzecz. Jeszcze wspanialszą jest to, że ECS pod spodem korzysta z wszystkich funkcjonalności pozostałych narzędzi. Spróbujmy dodać flagę --fix do polecenia.
  • 28. Magia! ECS poprawił wszystkie znalezione błędy oprócz nieszczęsnej zadeklarowanej, ale nigdzie nie używanej zmiennej. Tę jedną zmianę trzeba zrobić ręcznie.
  • 29. Dobra rada Jeżeli dodajecie ECS do istniejącego projektu, szczególnie sporych rozmiarów, warto uruchamiać --fix na osobnym branchu, żeby przez przypadek nic nie popsuć, a jednocześnie przy commitowaniu sprawdzić wszystkie zmiany.
  • 30. PSALM
  • 31. Kilka przemyśleń ● styl stylem, ale każdemu programiście zdarza się zrobić nieprzemyślany błąd ● błąd może być “głupi”, na przykład if do którego nie da się nigdy wejść ● ale czasami może to być coś bardziej skomplikowanego i na pierwszy rzut oka niewykrywalnego jak korzystanie z pól niezadeklarowanych w konstruktorze
  • 32. Co nam daje Github?
  • 34. Prosta instalacja composer require vimeo/psalm --dev Using version ^4.1 for vimeo/psalm ./composer.json has been updated Running composer update vimeo/psalm Loading composer repositories with package information Updating dependencies Lock file operations: 13 installs, 0 updates, 0 removals - Locking amphp/amp (v2.5.0) - Locking amphp/byte-stream (v1.8.0) - Locking dnoegel/php-xdg-base-dir (v0.1.1) - Locking felixfbecker/advanced-json-rpc (v3.1.1) - Locking felixfbecker/language-server-protocol (v1.5.0) - Locking netresearch/jsonmapper (v2.1.0) - Locking openlss/lib-array2xml (1.0.0) - Locking phpdocumentor/reflection-common (2.2.0) - Locking phpdocumentor/reflection-docblock (5.2.2) - Locking phpdocumentor/type-resolver (1.4.0) - Locking vimeo/psalm (4.1.0) - Locking webmozart/assert (1.9.1) - Locking webmozart/path-util (2.3.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 13 installs, 0 updates, 0 removals - Downloading webmozart/assert (1.9.1)
  • 35. Proste uruchomienie… a jednak nie? ./vendor/bin/psalm Could not locate a config XML file in path /application/. Have you run 'psalm --init' ? ./vendor/bin/psalm --init Calculating best config level based on project files Scanning files... Analyzing files... E░ Detected level 2 as a suitable initial default Config file created successfully. Please re-run psalm.
  • 36. Kilka przemyśleń ● Psalm znajduje mnóstwo rzeczy, ale niestety nie na wszystkie mamy wpływ ● warto zastanowić się co naprawdę jest istotne i te mniej ważne sprawy ignorować przez pole issueHandlers w pliku konfiguracyjnym psalm.xml
  • 37. Problem z laravelowymi Commandami? ./vendor/bin/psalm Scanning files... Analyzing files... E░░░░░░░░░░I░░I░░░░░░░░░░░░░░░░░░░░░░░II░░░I░░░░░░░░░░░░░░░░ 60 / 78 (76%) ░░░░░░░░░░░░░░░░░░ ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property BrewmapConsoleCommandsImportGoogleMap::$laravel is not defined in constructor of BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074) class ImportGoogleMap extends Command ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property BrewmapConsoleCommandsImportGoogleMap::$name is not defined in constructor of BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074) class ImportGoogleMap extends Command ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property BrewmapConsoleCommandsImportGoogleMap::$input is not defined in constructor of BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074) class ImportGoogleMap extends Command ERROR: PropertyNotSetInConstructor - app/Console/Commands/ImportGoogleMap.php:16:7 - Property BrewmapConsoleCommandsImportGoogleMap::$output is not defined in constructor of BrewmapConsoleCommandsImportGoogleMap and in any methods called in the constructor (see https://psalm.dev/074) class ImportGoogleMap extends Command
  • 38. Problem z laravelowymi Commandami? <issueHandlers > <PropertyNotSetInConstructor > <errorLevel type="suppress" > < directory name="app/Console/Commands" /> < directory name="app/Nova/Metrics" /> < directory name="app/Http/Requests/" /> < directory name="app/Nova/Tools" /> < directory name="database/factories" /> </errorLevel> </PropertyNotSetInConstructor > </issueHandlers >
  • 39. Kilka przykładów ● no to siup, obejrzyjmy kilka przykładów:
  • 40. Głupie ify if (false) { die(); } INFO: TypeDoesNotContainType - app/Console/Commands/ImportGoogleMap.php:30:12 if (false) is impossible (see https://psalm.dev/056 ) if(false) {
  • 41. Nieużywany kod class Localize { protected array $locales = ["en", "pl"]; protected Application $application; protected string $legacy = ""; public function __construct(Application $application) { $this->application = $application; } ERROR: PossiblyUnusedProperty - app/Http/Middleware/Localize.php:16:22 Cannot find any references to property BrewmapHttpMiddlewareLocalize::$legacy (see https://psalm.dev/149 ) protected string $legacy = "";
  • 42. Głupotki tablicowe protected function validateCheckSum(): void { $sum = 0; for ($i = 0; $i < 10; $i++) { $sum += self::CHECKSUM_WEIGHTS[$i] * $this->value[$i]; } $checkSum = 10 - $sum % 10; $checkNumber = $checkSum == 10 ? 0 : $checkSum; ERROR: InvalidOperand - app/Providers/Validators/Pesel.php:72:50 Cannot perform a numeric operation with a non-numeric type string (see https://psalm.dev/058 ) $sum += self::CHECKSUM_WEIGHTS[$i] * $this->value[$i] ;
  • 43. Niewłaściwe argumenty public function render($request, Exception $exception): Response { $status = $exception->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR; // (...) $response = response()->json($body, $status, [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $this->logResponse($response); return $response; } ERROR: InvalidScalarArgument - app/Exceptions/Handler.php:95:4 5 Argument 2 of IlluminateContractsRoutingResponseFactory::json expects int, int|string provided (see https://psalm.dev/012 ) $response = response()->json($body, $status, [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  • 44. Źle zwracane typy use IlluminateHttpRedirectResponse ; // (...) public function redirectToFacebook (): RedirectResponse { return Socialite::driver("facebook")->redirect(); } INFO: LessSpecificReturnStatement - app/Http/Controllers/API/AuthenticationController.php:36:16 The type 'SymfonyComponentHttpFoundationRedirectResponse' is more general than the declared return type 'IlluminateHttpRedirectResponse' for BrewmapHttpControllersAPIAuthenticationController::redirectToFacebook (see https://psalm.dev/129 ) return Socialite::driver("facebook")->redirect() ;
  • 45. Źle zwracane typy // GuzzleHttp/Client::_call() returns PromisePromiseInterface class MockGuzzleClient extends Client { public function __call($method, $args): ResponseInterface { return new Response(); } } ERROR: ImplementedReturnTypeMismatch - app/Manager/MockGuzzleClient.php:20:16 The inherited return type 'GuzzleHttpPromisePromiseInterface' for GuzzleHttpClient::__call is different to the implemented return type for CalclyCoreManagerMockGuzzleClient::__call 'PsrHttpMessageResponseInterface' (see https://psalm.dev/123 ) * @return ResponseInterface
  • 46. Źle nazwane parametry metod class Kernel extends ConsoleKernel { protected function commands(): void { $this->load(__DIR__ . "/Commands"); } protected function renderException($output, Throwable $exception) { parent::renderException($output, $extension); } } INFO: ParamNameMismatch - app/Console/Kernel.php:17:59 Argument 2 of BrewmapConsoleKernel::renderException has wrong name $exception, expecting $e as defined by IlluminateFoundationConsoleKernel::renderException (see https://psalm.dev/230 ) protected function renderException($output, Throwable $exception)
  • 47. Wbudowana integracja z PHPStormem!
  • 49. composer.json { "scripts": { "post-autoload-dump" : [ "Illuminate FoundationComposerScripts::postAutoloadDump" , "@php artisan package:discover --ansi" ], "psalm": "./vendor/bin/psalm" , "behat": "./vendor/bin/behat --format=progress" , "ecs": "./vendor/bin/ecs check" , "check": [ "composer psalm" , "composer behat" , "composer ecs" ] } }
  • 51. A może Github Workflow? name: Behaviour, code and style test on: push: branches: [ "develop" ] pull_request : branches: [ "develop" ] jobs: build: # (...) - name: Run test suite run: docker-compose run php composer behat - name: Run code style checker run: docker-compose run php composer ecs - name: Run code static analysis run: docker-compose run php composer psalm