Aby se předešlo zbytečnému mrzení při vývoji, hodí se vytvořit první commit obsahující následující výchozí composer.json s aktualizovanými údaji. Vysvětlení jednotlivých částí je rozebráno v další části dokumentu.

composer.json

{
    "name": "interitty/test",
    "description": "Description of the Test package",
    "homepage": "https://gitlab.com/interitty/test",
    "keywords": ["interitty"],
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Pavel Železný",
            "email": "[email protected]"
        }
    ],
    "support": {
        "email": "[email protected]",
        "issues": "https://gitlab.com/interitty/test/-/issues",
        "source": "https://gitlab.com/interitty/test"
    },
    "require": {
        "php": "~8.3",
        "dg/composer-cleaner": "~2.2"
    },
    "config": {
        "classmap-authoritative": true,
        "sort-packages": true,
        "allow-plugins": {
            "dg/composer-cleaner": true
        }
    },
    "extra": {
        "branch-alias": {
            "dev-main": "1.x-dev"
        }
    }
}

Dále by zde měl být soubor README.md obsahující alespoň název a popis balíčku, doplněný o přehled závislostí (verze PHP, frameworku, rozšíření php, …) a postup jak balíček instalovat. Příklad takového souboru je s aktualizovanými údaji následující:

README.md

# Test #

Description of the Test package

## Requirements ##

- [PHP](https://php.net/) >= 8.3

## Installation ##

The best way to install [**interitty/test**](https://gitlab.com/interitty/test) is using [Composer](https://getcomposer.org/):

```bash
composer require interitty/test
```

Hned další soubor, který nesmí chybět v základním balíčku je LICENSE.md, který by měl obsahovat aktualizovanou verzi použité licence, která se vztahuje k danému balíčku. Po všech rozborech se pro běžný open-source jeví jako nejvhodnější licence MIT. Příklad takového souboru je s aktualizovanými údaji následující:

LICENSE.md

Copyright 2018-2024 Pavel Železný

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to following conditions:

The above copyright of the Software.

THE SOFTWARE IN THE SOFTWARE.

Nejen kolegové programátoři obvykle ocení, když snadno a rychle poznají, co se v jednotlivých verzích zdrojového kódu aplikace změnilo. Někteří jsou snad i trochu zvědaví, co se chystá a pro tyto účely je zde soubor CHANGELOG.md. Pro strukturu jeho obsahu existuje doporučení. Ve výchozím stavu pak může soubor vypadat následovně:

CHANGELOG.md

# Changelog #
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased] ##

### Added ###

### Changed ###

### Fixed ###

Aby se do GIT repozitáře nekopírovala zbytečně složka vendor se zdrojovými kódy závislostí třetích stran, která navíc obsahuje soubory vývojové verze, její obsah zastarává rychleji než samotný balíček a také aby byl výsledný repozitář menší a tedy i rychlejší, je vhodné přidat i soubor .gitignore který tuto složku vyřadí ze sledování změn. Příklad takového souboru je následující:

.gitignore

/vendor

Verzovací systémy GitLab, GitHub a asi i jiné vytváří automaticky při přidání tagu verze soubor .zip, který je k dispozici k přímému stažení. Tento soubor ve výchozím stavu neobsahuje data předchozích verzí, tedy složku .git. Celkově tento soubor slouží výhradně pro produkční užití. Nedává tedy smysl, aby obsahoval soubory a složky související s vývojem a tedy i s verzováním. Informaci, které soubory a složky vynechat při vytváření archivu je potřeba předat v souboru .gitattributes. Příklad takového souboru je následující:

.gitattributes

.gitattributes export-ignore
.gitignore export-ignore

Dle doporučení NÚKIB by každý balíček měl ještě obsahovat soubor SECURITY ve kterém by měly být informace, kam se mají hlásit případné bezpečnostní incidenty. Obsah tohoto souboru podléhá RFC-9116 a s jeho generováním může pomoci například služba securitytxt.org. Dle doporučení Michala Špačka by měl být soubor ještě digitálně podepsán příkazem gpg --clear-sign SECURITY, aby bylo možné ověřit jeho pravost. Příklad takového souboru je s aktualizovanými údaji následující:

SECURITY

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Contact: https://gitlab.com/interitty/test/-/issues
Contact: mailto://[email protected]
Contact: https://pavelzelezny.cz/index.cs.html#contacts
Contact: https://m.me/pavel.zelezny
Canonical: https://gitlab.com/interitty/test/-/blob/main/SECURITY
Expires: 2024-12-31T23:59:59+02:00
Encryption: https://pavelzelezny.cz/key.asc
Preferred-Languages: cs, en
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEEAGg+1oN/RkvP8kyoOgaveP2wz8FAmWO4vEACgkQoOgaveP2
wz/6tw/+IwO1mDDrpE68vnrBm5wAQfzvElAJpTpMbHghXqta01Hf7obO2NSFnO6D
hhFv6JYealvA7Ibmry5d6m2oNgoPfmkOHyNwQ8Tfunm+SYppxzqZVr0bHClL5Cp0
6GWgOKkPoApIqU5NHOSjgiP8yAwuOX0IfM2HTA4ZoryvLmaf0zi5LOpG+Hkc+Okg
d5BCDFKplNH9ecAKcZhQnVv9SxgrxZJH/45JyT335qVou4kcJ8y0qVcy20Peph8w
MG0vjUMq/we+ngH6Zt82Atra4uf7D22TIpo1QKIdTuAC/tH8oht8Y+YWXVfetNql
As4jXKkBGZarTCRYpmCt+E6/G9DcqHevIcsH7704FjlC1Nrbf5zfJ1T0+dVWA3qK
tkFdxGZEPKGPmynqzZjmFMMavKjxJeY/vJwrOacdoVW4iOaz6zycoubdIMB4fs5G
lLKWypoqhyv4NCVFmOKTIaJ+PWoxJRmD16ykKYWjQ6fDt105sJqd9P6QK64/QUIE
H4QJYcYKXo2qsS04k7pJty5HkloaQWma4RsDJ8ri61v1IAF+JOOcApsnjVvQvCxD
NzFl2MS/Oesqd4vwk6Jrtj1T1f3Rw+BtpXvb7ssSofPsD9lylrl8U9reEaeNyid2
ll1VwX4GQieBPtvmPB5Ff3Q63nluuiYMQAqxd7sLLP6z8aYBLHQ=
=Cdba
-----END PGP SIGNATURE-----

Composer update

I když se jedná prakticky o zcela prázdný commit jen s úvodní definicí Composeru a pár dalšími soubory, je potřeba sestavit výstupní composer.lock soubor. Toho lze dosáhnout nejlépe následujícím příkazem spuštěným v kořenovém adresáři projektu: rm -f ./composer.lock && rm -rf ./vendor && composer clearcache && composer update.
Příkaz zároveň vymaže composer cache, aby se předešlo případné chybě použití staré lokální verze balíčků.

Výše popsané je v podstatě společné pro všechny balíčky řízené balíčkovacím systémem Composer. Stačí tedy takto připravený obsah složky, včetně příkazem composer update vygenerovaného soubory composer.lock, umístit do prvního GIT commitu s popisem Initial commit.

Druhý commit

V druhém commitu se již obvykle přidává první část práce. Ta se umisťuje do složky /src. Pokud se jedná o kód v jazyce PHP, pak by měl být rozhodně doplněn jednotkovými testy ve složce /tests. Pro ně se obvykle používá nástroj PHPUnit. To však vyžaduje mírnou úpravu obsahu prvního commitu. Do již existujících souborů přibude mimo jiné následující obsah:

composer.json

{
    "autoload": {
        "psr-4": {
            "Interitty\\Test\\": [
                "src/Test"
            ]
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Interitty\\": [
                "tests"
            ]
        }
    },
    "config": {
        "allow-plugins": {
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "phpstan/extension-installer": true
        }
    },
    "require-dev": {
        "interitty/code-checker": "~1.0",
        "interitty/phpunit": "~1.0"
    },
    "extra": {
        "cleaner-ignore": {
            "phpunit/phpunit": [
                "phpunit.xsd",
                "schema"
            ]
        }
    }
}

Code checker git-hook

Součástí výše popsané úpravy souboru composer.json je mimo jiné instalace balíčku interitty/code-checker který dokáže zajistit průběžnou jednotnou kontrolu kvality aplikačního kódu. Tyto kontroly lze spouštět ručně, nebo automaticky při pokusu o uložení nebo odevzdání práce do verzovacího systému GIT.

Pro zapnutí automatické kontroly na lokálním počítači je potřeba ještě spustit následující příkaz v kořenové složce projektu:

./vendor/bin/git-hook --install

.gitattributes

tests/ export-ignore
phpunit.xml export-ignore

A pro účely PHPUnit případně přibydou ještě následující soubory:

phpunit.xml

<?xml version="1.0"?>
<phpunit
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
    bootstrap="vendor/autoload.php"
    colors="true"
    printerClass="Sempro\PHPUnitPrettyPrinter\PrettyPrinterForPhpUnit9"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    stopOnError="false"
    stopOnFailure="false"
    stopOnIncomplete="false"
    stopOnSkipped="false"
    stopOnRisky="false"
    verbose="true">
    <coverage processUncoveredFiles="true">
        <include>
            <directory suffix=".php">src</directory>
        </include>
        <exclude>
            <directory suffix="Interface.php">src</directory>
        </include>
    </coverage>
    <testsuites>
        <testsuite name="All tests">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
</phpunit>

Výchozí Composer balíček

V rámci vývoje v jazyce PHP se programátor dříve či později dostane k existenci a následně i potřebě vytvoření vlastního Composer balíčku.
K tomuto účelu standardně slouží příkaz composer init, který uživatele provede procesem založení základního Composer balíčku. Výsledkem takového procesu je soubor composer.json obvykle podobný následujícímu kódu

{
    "name": "interitty/test",
    "description": "",
    "type": "library",
    "license": "MIT",
    "autoload": {
        "psr-4": {
            "Interitty\\Test\\": "src/"
        }
    },
    "authors": [
        {
            "name": "Pavel Zelezny",
            "email": "[email protected]"
        }
    ],
    "require": {}
}

Aby uživatel nemusel při každém volání neustále vypisovat ty stejné údaje, je možné nastavit zatím nezdokumentované systémové proměnné.

export COMPOSER_DEFAULT_AUTHOR="Pavel Železný"
export COMPOSER_DEFAULT_EMAIL="[email protected]"
export COMPOSER_DEFAULT_LICENSE="MIT"
export COMPOSER_DEFAULT_VENDOR="interitty"

Composer.json atributy

Jedná se o veskrze o naprosto minimální verzi tohoto souboru. V průběhu praxe se však ukázalo jako nezbytné, nebo při nejmenším vhodné doplnit ještě následující atributy

Klíčová slova

Pole klíčových slov, podle kterých se orientuje repozitář Composer balíčků Packagist.

{
    "keywords": ["interitty"]
}

Podporovaná verze php

Příznak, jaké verze jazyka PHP jsou podporované. Aby se někdo nepokoušel moderní kód spustit na prehistorické verzi a nereportoval tak zbytečné chyby.

{
    "require": {
        "php": "~8.3"
    }
}

Řazení požadovaných balíčků dle abecedy

Když programátor přidá závislost na Composer balíčku pomocí příkazu composer require, umístí se tento obvykle na konec seznamu požadovaných balíčků. Když je v průběhu času potřeba provést kontrolu závislostí, je v takovém seznamu obtížnější se zorientovat. Jednodušší je řazení abecední, které napomáhá rychlejší orientaci. Toto chování je však potřeba ručně zapnout

{
    "config": {
        "sort-packages": true
    }
}

Statická kontrola kvality kódu

Výhoda jazyka PHP je, že nenutí programátorovi žádnou náročnou syntaxi zdrojového kódu. Je to však rovněž velká nevýhoda. Jakmile má s kódem pracovat více lidí, je při nejmenším vhodné si domluvit a dodržovat jednotný standard. Jedním takovým všeobecně uznávaným standardem je PSR.

Při sebevětší snaze tento standard dodržovat se však vždy může vyskytnout chyba. Pro tyto účely existují nástroje squizlabs/php_codesnifferfriendsofphp/php-cs-fixer, které umožňují kontrolovat a opravovat kód dle domluveného standardu.

Kvalitní kód se také vyznačuje dodržováním základních metrik kvalitního kódu, jako například nízká hodnota cyklomatické složitosti. Pro kontrolu těchto metrik slouží nástroj phpmd/phpmd.

Moderní verze jazyka PHP 8 umožňuje psát zdrojový kód takzvaně Striktně typovaný. Díky tomu lze strojově kontrolovat, zda je kód v pořádku a že například jedna funkce nemůže vracet i něco jiného, než si programátor myslí. Pro kontrolu slouží nástroj phpstan/phpstan.

V neposlední řadě je vhodné, aby chování jednotlivých částí aplikace bylo strojově kontrolováno a předcházelo se tak zbytečným chybám. Jedním z obvyklých nástrojů pro takovou kontrolu je PHPUnit.

Prakticky při každé změně zdrojového kódu však může nastat nedostatek proti kterémukoliv výše popsanému pravidlu a proto by se měly spouštět vždy všechny testy při pokusu o odeslání kódu do GIT repozitáře. Je však až příliš snadné opomenout spuštění některého z testů. Aby se takovému nedostatku předešlo, byl vytvořen nástroj interitty/code-checker který umožňuje spouštět všechny kontroly najednou a to dokonce i pomocí git hook, čímž se zajistí kontrola ještě před commitnutím nebo odesláním zdrojového kódu.

{
    "require-dev": {
        "interitty/code-checker": "~1.0"
    },
    "config": {
        "allow-plugins": {
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "phpstan/extension-installer": true
        }
    }
}

Umožnění přidání závislosti na vývojové verzi balíčku

V určitých fázích vývoje se může stát, že je potřeba přidat závislost na teprve chystané verzi balíčku. V situaci, kdy je kód sice považován za hotový, neb je v produkční větvi kódu, ale ještě nebyla zveřejněna novější verze (která by ho obsahovala), může se hodit ukazatel verze směřující na nejnovější commit v produkční větvi (main).

{
    "extra": {
        "branch-alias": {
            "dev-main": "1.x-dev"
        }
    }
}

Výkonnostní optimalizace autoloaderu

Na rozdíl na příklad od nette/robot-loader který se snaží vývojáři co nejvíce ulehčit práci a promíjí některé chyby, jako například nedodržování PSR standardůComposer autoloader jen omezené možnosti, jako například odhadování výskytu třídy v souboru a cestě právě dle určeného standardu. To se může hodit pro vývoj, ale v provozu to má negativní vliv na celkovou rychlost aplikace.

Z praxe se tak osvědčilo všechna tato ulehčení naopak vypnout a při přidání nové třídy (což se neděje zase až tak moc často v poměru proti běžnému vývoji a údržbě) nechat sestavit mapu tříd ručně zavoláním příkazu composer dump-autoload. Po celý zbytek vývoje tak bude aplikace rychlejší díky zapnutí optimalizovaného autoloaderu pomocí direktivy classmap-authoritative.

{
    "config": {
        "classmap-authoritative": true
    }
}

Vyžadovaná rozšíření PHP

Při psaní PHP Coding Standard Fixer kódu aplikace se občas hodí použít funkce, které nejsou přímo vestavěné, ale jsou součástí některého rozšíření, které obvykle bývá, ale nemusí vždy být k dispozici. Příkladem takové funkce může být například mb_strlen která je součástí rozšíření ext-mbstring

{
    "require": {
        "ext-mbstring": "*"
    }
}

Existuje nástroj dg/php-extensions-finder který dokáže analyzovat zdrojový kód a identifikovat, která rozšíření jsou použita a měla by být tedy doplněna do kontrolního mechanismu Composeru.

Úklid závislostí

Jednotlivé závislosti s sebou nesou množství doprovodných souborů, které nejsou nezbytně nutné pro běh aplikace. Hodí se pro účely vývoje, ale v produkčním prostředí mohou představovat zbytečné bezpečnostní riziko. Jedná se o různé testy, dokumentace, nepoužívané slovníky a další. I na vývojovém prostředí mohou mít tyto soubory negativní vliv na celkovou rychlost aplikace, neb se zde vyskytující PHP třídy přidávají do celkového seznamu autoloaderu.

Velmi efektivním nástrojem pro řešení tohoto úkolu je nástroj dg/composer-cleaner který stačí přidat mezi požadované závislosti a on už se postará o zbytek.

{
    "require": {
        "dg/composer-cleaner": "~2.2"
    },
    "config": {
        "allow-plugins": {
            "dg/composer-cleaner": true
        }
    }
}

Občas se však stane, že smaže i něco, co by se mazat nemělo. Není to úplně obvyklé, ale stát se to může. Obvyklým příkladem jsou nezbytné soubory nástroje PHPUnit. Takové soubory je poté přidat do extra nastavení.

{
    "extra": {
        "cleaner-ignore": {
            "phpunit/phpunit": [
                "phpunit.xsd",
                "schema"
            ]
        }
    }
}