Manual:Coding conventions/PHP/cs

Tato stránka popisuje konvence které se používají při psaní u PHP skriptů v. Viz také, které platí pro všechny programovací jazyky včetně PHP. Pokud byste chtěli krátký kontrolní seznam, který vám pomůže zkontrolovat vaši tvorbu, zkuste použít.

Většinu pravidel stylu kódu lze automaticky opravit nebo alespoň detekovat pomocí PHP_CodeSniffer (aka PHPCS) pomocí vlastní sady pravidel pro MediaWiki. Další informace viz.



Mezery
Pro MediaWiki je prvořadým úkolem srozumitelnost a přehlednost kódu. Proto se při psaní hojně využívají mezery.

Odsazení provádějte tabulátory, nikoli mezerami. Omezte řádky na 120 znaků (při šířce tabulátoru 4 znaky).

U binárních operátorů, přidejte z každé strany jednu mezeru, tak aby byly naprosto jasně odděleny. Viz příklad:

Uvnitř umístěte mezery vedle závorek, kromě případů, kdy jsou závorky prázdné. Za názvem funkce nevkládejte mezeru.

Vložte mezeru za  v nápovědě k návratovému typu funkce, ale ne před:

Mezeru do závorek vložte jen pokud chcete deklarovat pole, které nemá být prázdné. Mezery také nepoužívejte při adresování jednotlivých prvků pole.

Za řídící prvky, jako je, , , , nebo klíčové slovo  naopak mezeru vložte vždy:

U deklarování typu proměnné nepoužívejte nikdy mezery. Ani v závorkách, ani za nimi:

U komentářů přidejte mezeru za nebo  a teprve pak napište svůj komentář.



Ternární operátor
může být velice užitečný, pokud jsou výrazy velmi krátké a zřejmé:

Pokud však uvažujete o víceřádkovém výrazu s ternárním operátorem, zvažte místo toho použití bloku. Pamatujte, že místo na disku je levné, čitelnost kódu je všechno, "if" je angličtina a " " ne. Pokud používáte víceřádkový ternární výraz, otazník a dvojtečka by měly být na začátku druhého a třetího řádku a nikoli na konci prvního a druhého (na rozdíl od konvence JavaScriptu MediaWiki).

Protože MediaWiki vyžaduje PHP (MW stabilní php požadavek) nebo novější, je použití shorthand ternárního operátora  známého také jako elvis operator, zavedeného v PHP 5.3, povoleno.

Od PHP 7.0 je k dispozici null coalescing operator (nulový koalescenční operátor) a v některých případech může nahradit ternárního operátora. Například místo  byste mohli napsat:



Literální řetězce
Jednotlivé uvozovky jsou preferovány ve všech případech, kdy jsou rovnocenné dvojitým uvozovkám. Kód používající jednoduché uvozovky je méně náchylný k chybám a snadněji se kontroluje, protože nemůže náhodně obsahovat únikové sekvence (escape sequences) nebo proměnné (variables). Například regulární výraz vyžaduje dodatečné zpětné lomítko, takže je o něco víc složitější a náchylnější k chybám než. Také pro lidi, kteří používají US/UK qwerty klávesnice, je snazší jej psát, protože není nutné mačkat Shift.

Nebojte se však použít funkci PHP interpolace do dvojitých uvozovek:

To má o něco lepší výkonové vlastnosti než ekvivalent pomocí operátoru zřetězení (tečka) a také to lépe vypadá.

Řetězce ve stylu heredoc jsou také někdy užitečné:

Někteří autoři rádi používají END jako koncový token, který je také názvem funkce PHP.



Funkce a jejich parametry
Vyhněte se předávání velkého počtu parametrů funkcím nebo konstrukcím:

Je nemožné rychle si zapamatovat pořadí parametrů a nevyhnutelně skončíte s nutností zakódovat všechna výchozí nastavení u volajících, abyste si přizpůsobili parametr na konci seznamu. Pokud vás láká kódovat takovou funkci, zvažte místo toho předání asociativního pole pojmenovaných parametrů.

Obecně se nedoporučuje ve funkcích používání booleovských parametrů. V případě, bez vyhledání dokumentace pro , je nemožné vědět, co mají tyto parametry označovat. Mnohem lepší je buď použít konstanty třídy a vytvořit obecný parametr příznaku:



Nebo, aby vaše funkce akceptovala řadu pojmenovaných parametrů:



Zkuste nepřepočítávat proměnné v průběhu funkce a vyhýbejte se úpravám parametrů předávaných funkci (pokud nejsou předány odkazem a to je samozřejmě smyslem celé funkce).



Výrazy přiřazení
Použití přiřazení jako výrazu je pro čtenáře překvapivé a vypadá to jako chyba. Nepište kód takto:

Prostor je levný a vy jste rychlý písař, takže místo toho použijte:

Použití přiřazení v klauzuli bývalo pro iteraci legitimní:

V novém kódu to není nutné. Místo toho použijte:



Výpůjčky z jazyka C
PHP je programovací jazyk, který byl navržen lidmi, kteří milovali programování v jazyce C a proto chtěli do PHP implementovat takovou syntaxi, na jakou byli zvyklí. Ovšem PHP se v několika významných detailech od jazyka C liší.

U jazyka C se konstanty implementují jako makra pro preprocesor a tím pádem se zpracovávají velmi rychle. U PHP, se ale vyhledává jméno konstanty v indexované tabulce což je mnohem pomalejší, než když se použije řetězcový literál. Ve většině případů, tam, kde byste použili v jazyce C množinu maker, je u PHP výhodnější použít řetězcové literály. U PHP, se ale vyhledává jméno konstanty v indexované tabulce což je mnohem pomalejší, než když se použije řetězcový literál. Ve většině případů, tam, kde byste použili v jazyce C množinu maker, je u PHP výhodnější použít řetězcové literály.

PHP má tři speciální literály, pro které je velká, či malá nebo smíšená velikost písma v jazyce nevýznamná (od PHP 5.1.3), ale pro kterou je naše konvence vždy malá:, a.

Použijte, nikoli. Mají jemně odlišný význam:

Ovšem z hlediska výkonu na tom bude druhá varianta hůř.



Alternativní syntaxe pro řídící struktury
PHP umožňuje pro řídicí struktury, jako,  , atp., používat alternativní syntaxi, při které se místo složených závorek použije dvojtečka:

Takovéto syntaxi byste se měli vyhnout velikým obloukem, protože řada textových editorů by se zobrazením takového kódu mohla mít problém. Takže raději použijte složené závorky:



Závorky
Více o závorkách najdete v části o odsazování a zarovnávání kódu na stránce věnované všeobecné konvenci pro psaní zdrojového kódu.

Pro anonymní funkce upřednostňujte šipkové funkce, pokud se anonymní funkce skládá pouze z jednoho řádku. Funkce šipek jsou stručnější a čitelnější než běžné anonymní funkce a úhledně odstraňují problémy s formátováním, které vznikají u jednořádkových anonymních funkcí.



Deklarování typů v parametrech funkce
Pokud je to možné, použijte [deklarace typu https://www.php.net/language.types.declarations] a [deklarace typu návratu https://www.php.net/language.types.declarations#example-80] (tipování typu).

Upozorňuji, že u starších verzí PHP (před 7.4), nelze obsloužit naznačování typu (ať již restriktivní, či volné) v rámci podtřídy.

Scalar typehints are allowed as of MediaWiki 1.35, following the switch to PHP 7.2 (T231710).

Use PHP 7.1 syntax for nullable parameters: choose

instead of

The former conveys precisely the nullability of a parameter, without risking any ambiguity with optional parameters. IDEs and static analysis tools will also recognize it as such, and will not complain if a non-nullable parameter follows a nullable one.

Konvence pro jména
Pro názvy funkcí a proměnných používejte notaci lower-CamelCase. Jak ukazuje příklad:

Pro názvy tříd používejte naopak Upper-CamelCase:. Použijte velká písmena s podtržítky pro globální konstanty a konstanty tříd:,. Jiné proměnné jsou obvykle malými písmeny (lowercase) nebo lowerCamelCase. Nepoužívejte podtržítka v názvech proměnných.

Používejte také zavedené prefixy. I ty napovídají oč v kódu jde:

Funkce

 * (wiki funkce) – funkce, dostupné na té nejvyšší úrovni, např.
 * (funkce rozšíření) = jsou globální funkce, napsané v rámci rozšíření. I když "většina skriptů napsaných v moderním stylu vkládá do svých tříd funkce, které se registrují jako háčky, jako statické metody třídy, aby na té nejvyšší úrovni do budoucna zbylo jen minimum, takto pojmenovaného kódu." (-- brion in Manual_talk:Coding_conventions)

V tomto případě se preferují slovesa, která jasně naznačí co má funkce udělat: Takže místo použijte. When exposing functions for use in testing, mark these as  per the Stable interface policy. Misuse or unofficial reliance on these is more problematic than most internal methods, and as such we tend to make these throw if they run outside of a test environment.

Proměnné

 * – globální proměnné, např. . Vždy to používejte pro nové globální, aby bylo snadné najít chybějící deklarace "". V rozšířeních by měl být název přípony použit jako oddělovač oboru názvů. Například a ne.
 * Globální prohlášení by měla být na začátku funkce, takže závislosti lze určit bez nutnosti číst celou funkci.

Je běžné pracovat s instancí třídy databáze. Máme pro ně konvenci pojmenování, která pomáhá sledovat povahu serveru, ke kterému jsme připojeni. Toto je obzvláště důležité v replikovaných prostředích, jako jsou Wikimedie a další velké wiki. Ve vývojových prostředích obvykle neexistuje žádný rozdíl mezi těmito dvěma typy, které mohou skrývat jemné odlišnosti.


 * – objekt  pro zápis (primární připojení)
 * – objekt  pro čtení, které není citlivé na souběžnost (může to být replika pouze pro čtení, mírně za primárním stavem, takže se nikdy nesnažte s ním zapisovat do databáze nebo získat "autoritativní" odpověď na důležité dotazy, jako jsou oprávnění a stav bloku)

Ve starém kódu lze vidět následující, ale v novém kódu se to nedoporučuje:


 * – proměnné relace, např.
 * – proměnné souborů cookie, např.
 * – proměnné příspěvku (odeslané prostřednictvím polí formuláře), např.
 * – proměnné členů objektu: . To je v novém kódu odrazováno, ale zkuste zůstat v rámci třídy konzistentní.

Funkce by měla být použita, pouze pokud chcete potlačit chyby. Jinak stačí použít  (booleovský převod).

Obecný případ použití: Volitelné booleovské konfigurační klíče, které mají výchozí hodnotu false.
 * essentially does.
 * Dejte si pozor na booleovské konverze.
 * Potlačuje chyby ohledně nedefinovaných vlastností a proměnných. Pokud chcete testovat pouze nedefinované, použijte . Pokud máte v úmyslu pouze otestovat "prázdné" hodnoty (např. false, zero, empty array, atd.), použijte.

Nepoužívejte k testování. Použití v této situaci může způsobit chyby skrytím chybně napsaných názvů proměnných. Místo toho použijte.



Booleovská konverze

 * Nepoužívejte  nebo  k testování, zda je řetězec nebo pole prázdný, protože PHP považuje  za falešné - ale  je platný název a platné uživatelské jméno na MediaWiki. Místo toho použijte   nebo.
 * Prostudujte si pravidla pro převod na booleovský výraz. Při přeměně řetězců na boolean buďte opatrní.

Ostatní

 * Array plus nečísluje klíče numericky indexovaných polí, takže . Pokud chcete, aby klíče byly přečíslovány, použijte :
 * Ujistěte se, že jste nastavili na  . Toto vás upozorní na nedefinované proměnné a další jemné gotchas, které bude PHP ignorovat. Viz též.
 * Při práci v čistě PHP souboru (např. ne v HTML šabloně) vynechejte koncové tagy . Tyto značky často způsobují problémy s koncovými mezerami a chybovými zprávami "záhlaví již odeslány" (headers already sent) (srovnání viz.  17642 a http://news.php.net/php.general/280796). V řízení verzí je běžné, že soubory mají na konci souboru nový řádek (který editoři mohou přidat automaticky), což by tuto chybu vyvolalo.
 * Nepoužívejte syntaxi zavedenou v 5.3. PHP možná zavedlo tuto funkci, ale to neznamená, že bychom ji měli používat.
 * Pokud nemusíte, neprovádějte odkaz při procházení pole. I tehdy si uvědomte možné důsledky. (viz https://www.intracto.com/en-be/blog/php-quirks-passing-an-array-by-reference/)
 * PHP umožňuje deklarovat statické proměnné i v rámci nestatické metody třídy. To v některých případech vedlo k drobným chybám, protože proměnné jsou sdíleny mezi instancemi. Pokud byste nepoužili vlastnost, nepoužívejte ani statickou proměnnou.



Operátory rovnosti
Buďte opatrní s operátory porovnávání dvojitého rovná se (double-equals comparison operators). Trojité rovná se je obecně intuitivnější a mělo by být upřednostňováno, pokud nemáte důvod k použití dvojitých rovná se.


 * je pravda (!)
 * je pravda (!)
 * je nepravdivý
 * Chcete-li zkontrolovat, zda jsou dva skaláry, které mají být číselné, stejné, použijte, např.  je pravda.
 * Chcete-li zkontrolovat, zda jsou dvě proměnné typu 'řetězec' a mají stejnou posloupnost znaků, použijte, například  je nepravda.

Watch out for internal functions and constructs that use weak comparisons; for instance, provide the third parameter to, and don't mix scalar types in   constructs.

Do not use Yoda conditionals.



Přesnost čísla JSON
JSON používá systém typu JavaScriptu, takže všechna čísla jsou reprezentována jako 64bitová čísla IEEE s pohyblivou řádovou čárkou. To znamená, že čísla ztrácejí přesnost, když se zvětšují, a to do té míry, že některá celá čísla jsou nerozeznatelná: Čísla nad 2^52 budou mít přesnost horší než ±0,5. Takže velké celé číslo se může nakonec změnit na jiné celé číslo. Chcete-li se tomuto problému vyhnout, řešte potenciálně velká celá čísla jako řetězce v JSON.



Pro a proti


Nepoužívejte vestavěnou serializaci
Integrovaný mechanismus serializace PHP (funkce  a  ) by neměl být použit pro data uložená (nebo načtená) mimo aktuální proces. Místo toho použijte serializaci založenou na JSON (pozor na úskalí). Toto jsou pravidla zavedená RFC T161647.

Důvod je dvojí: (1) data serializovaná tímto mechanismem nemohou být spolehlivě unserializována s novější verzí stejné třídy. A (2) vytvořená sériová data lze použít ke spuštění škodlivého kódu, což představuje vážné bezpečnostní riziko.

Někdy váš kód nebude řídit mechanismus serializace, ale bude používat nějakou knihovnu nebo ovladač, který je používán interně. V takových případech je třeba podniknout kroky ke zmírnění rizika. První výše uvedený problém lze zmírnit převedením jakýchkoli dat na pole nebo prosté anonymní objekty před serializací. Za druhé lze snad riziko zmírnit pomocí funkce whitelisting, kterou PHP 7 zavádí pro unserializaci.



Komentáře a dokumentace
Je nezbytné, aby váš kód byl dobře zdokumentován, aby ostatní vývojáři a opraváři chyb mohli snadno pochopit logiku vašeho kódu. Nové třídy, metody a proměnné členy by měly zahrnovat komentáře poskytující stručné popisy jejich funkčnosti (pokud to není zřejmé), i když soukromé. Kromě toho by všechny nové metody měly dokumentovat jejich parametry a návratové hodnoty.

Při dokumentaci využíváme značky a styl, podobný jako když se používá Doxygen (ostatně PHPDoc z něj vychází, takže se jejich styl navzájem moc neliší). Díky tomu lze dokumentaci automaticky generovat rovnou z komentářů v kódu (viz ). Začněte blok Doxygenových komentářů pomocí, namísto formátování ve formátu Qt. Strukturální příkazy doxygen začínají. (Jako únikový znak použijte spíše  než   - oba styly fungují v Doxygenu, ale pro zpětnou a budoucí kompatibilitu si MediaWiki zvolila   styl.) Organizují vygenerovanou dokumentaci (pomocí ) a identifikují autory (pomocí značek  ).

Popisují funkci nebo metodu, parametry, které potřebuje (pomocí ) a to, co funkce vrací (pomocí  ). Formát parametrů je:

@param type $paramName Description of parameter

Pokud parametr akceptuje různé typy, použijte k jejich oddělení znak svislé čáry, neboli svislítka '|' (pipe), podobně jako u následujícího příkladu:

V popisu pokračujte na dalším řádku, odsazeném o jednu mezeru navíc.

Pro každé veřejné rozhraní (metoda, třída, proměnná, cokoli), které přidáte nebo změníte, zadejte značku, aby lidé rozšiřující kód prostřednictvím tohoto rozhraní věděli, že porušují kompatibilitu se starší verzí kódu.

FIXME obvykle znamená, že je něco špatného nebo zničeného. TODO znamená, že je zapotřebí vylepšení. Neznamená to nutně, že to bude osoba, která přidává komentář. HACK znamená, že bylo provedeno rychlé, ale nevhodné, nepříjemné nebo jinak suboptimální řešení okamžitého problému a nakonec by mělo být provedeno důkladnější přepsání kódu.



Záhlaví zdrojových souborů
Chcete-li vyhovět většině licencí, měli byste mít v horní části každého zdrojového souboru něco podobného následujícímu (specifické pro aplikace GPLv2).

<span id="Doxygen_tags">

Značky pro Doxygen
V našich zdrojových kódech používáme při vkládání poznámek následující značky, s nimiž pracuje Doxygen. Aby byla zajištěna konzistence vašeho souboru, zachovejte prosím při jejich použití i vy stejné pořadí, v jakém jsou uvedeny zde:

Poznámky k souboru:
 * @file

Třídy a jejich vlastnosti:


 * @todo
 * @var
 * @stable, @newable, @deprecated, @internal, @private
 * @see
 * @since
 * @ingroup
 * @param
 * @return
 * @throws
 * @author

<span id="Test_annotations">

Poznámky k testům
U testů používáme, mimo jiné, i následující poznámky. V tomto případě se nejedná o dokumentaci, protože mají význam pouze pro PHPUnit a svůj význam mají pouze při testování.


 * @depends
 * @group
 * @covers
 * @dataProvider

Integrace kódu
V kódu MediaWiki najdete i kódy, které jsou na ní zcela nezávislé a tím pádem je lze snadno implementovat do jiných aplikací. Zatímco některé z nich nyní existují jako oddělené knihovny, jiné zůstávají ve zdrojovém stromu MediaWiki (např. soubory v ). Kromě toho by měl být kód integrován do zbytku prostředí MediaWiki a měl by umožňovat, aby se s ním na oplátku integrovaly další oblasti codebase.

Viditelnost
Svoje metody (funkce) si nastavte jako public/protected/private (podle toho jak uznáte za vhodné). Ale není důvod k tomu, aby byly veřejné (public) všechny!

<span id="Global_objects">

Globální objekty
Nepřistupujte k superglobálním proměnným PHP jako, , atp. přímo. Získávejte jejich hodnotu raději přes volání metody. Většinou jsou k dispozici různé metody, které vám umožní získat požadovanou hodnotu ve formě typu, jaký zrovna potřebujete. Takže hodnotu pro aktuální můžete zjistit z nejbližšího objektu, takže není vůbec nutné kvůli tomu volat. Stejně tak nepřistupujte přímo k. Použijte, pokud chcete získat IP adresu aktuálního uživatele.

<span id="Static_methods_and_properties">

Statické metody a jejich vlastnosti
Ve svém PHP statické metody a vlastnosti klidně používejte, jenom mějte neustále na paměti, jaký je rozdíl mezi  a. bude vždy odkazovat na funkce a proměnné v rámci třídy, zatímco  bude odkazovat na konkrétní podtřídu, která ji zavolá. Pro víc podrobností si přečtěte v dokumentaci k PHP o dědičnosti a pozdějších statických vazbách.

Třídy
Zapouzdřete svůj kód do objektově orientované třídy, nebo ho přidejte jako novou funkci do třídy již existující. Nepřidávejte nové globální funkce či proměnné, pokud to není nezbytné. Snažte se mít neustále na paměti rozdíl mezi třídami, které patří k 'backendu', jakou jsou například entity které pracují s databází, jako,  ,  , aj. A třídami co obsluhují 'frontend', jako,  ,   a jiné. Pokud není váš kód zrovna objektově orientovaný, je lepší jej umístit alespoň do statické třídy, jako jsou např. třídy  nebo.

Jako ochrana před nedostatkem soukromých členů třídy a metod v PHP 4 bude starší kód označen komentáři, jako je, které označují záměr; respektujte to, jako by to vynutil tlumočník.

Označte nový kód správnými modifikátory viditelnosti, včetně, pokud je to vhodné, ale nepřidávejte viditelnost existujícímu kódu bez předchozí kontroly, testování a refaktoringu podle potřeby. Obecně je dobré vyhnout se změnám viditelnosti, pokud neprovádíte změny ve funkci, které by stejně narušily její stará použití.

<span id="Error_handling">

Jak ošetřit chyby
Obecně byste neměli chyby PHP potlačovat. Správná metoda řešení chyb je skutečně chyby řešit.

Pokud například uvažujete o použití operátoru potlačení chyb k potlačení upozornění na neplatný index pole, měli byste místo toho provést kontrolu indexu pole, než se k němu pokusíte přistupovat. Pokud je to možné, vždy zachyťte nebo přirozeně zabraňte chybám PHP.

Pouze pokud nastane situace, kdy očekáváte nevyhnutelné varování PHP, můžete použít operátor PHP. To platí pro případy, kdy:


 * 1) Je nemožné předvídat chybu, která se chystá.
 * 2) Chystáte se na řešení chyby vhodným způsobem poté, co k ní dojde.

K varování před použitím operátoru at-operator používáme PHPCS. Pokud jej opravdu potřebujete použít, budete také muset instruovat PHPCS, aby udělal výjimku, například:

Příkladem použití je otevření souboru pomocí. Chybu se můžete pokusit předpovědět voláním  a , ale na rozdíl od   takové operace se soubory zvyšují značně režii a vytváří nestabilní kód. Soubor může být například smazán nebo změněn mezi kontrolou a skutečným voláním  (viz TOC/TOU ).

V tomto případě zapište kód a vyzkoušejte pouze hlavní operaci, kterou musíte provést. Potom vyřešte případ, kdy se soubor neotevře, pomocí operátoru, abyste zabránili reakci PHP, a poté zkontrolujte výsledek. Pro  a   to znamená zkontrolovat booleovský falešný návrat a poté provést nouzový návrat nebo vyvolat výjimku.

Volné
Pro PHP 5 a starší vývojáři MediaWiki nedoporučovali používat operátor, protože způsoboval nepřihlášené a nevysvětlené fatální chyby (r39789). Místo toho se používali vlastní a  metody z knihovny. Důvodem je, že operátor at způsobil, že PHP neposkytovalo chybové zprávy nebo trasování zásobníku při fatálních chybách. Zatímco at-operator je určen hlavně pro nezávažné chyby (nikoli výjimky nebo fatální chyby), pokud by k fatální události došlo, znamenalo by to velmi špatný vývojářský zážitek.

V PHP 7 byla opravena obsluha výjimek (příklad), aby vždy poskytovala takové chyby, včetně trasování zásobníku, bez ohledu na potlačení chyb. V roce 2020 bylo používání AtEase zahájeno postupným ukončováním a obnovením provozovatele at-operátora. (T253461)

Exception handling
Exceptions can be checked (meaning callers are expected to catch them) or unchecked (meaning callers do not need to catch them).

Unchecked exceptions are commonly used for programming errors, such as invalid arguments passed to a function. These exceptions should generally use (either directly or by subclassing) the SPL exception classes, and do not need be documented with  annotations.

Checked exceptions, on the other hand, should always be documented with  annotations. When calling a method that can throw a checked exception, said exception should either be caught, or documented in the caller's doc comment. Checked exceptions should generally use dedicated exception classes extending. It's recommended not to use SPL exceptions as base classes for checked exceptions, so that correct usage of exception classes can be enforced with static code analyzers.

The base  class should never be thrown directly: use more specific exception classes instead. It can be used in a  clause if the intention is to catch all possible exceptions, but   is usually more correct for that purpose.

In legacy code it is relatively common to throw or subclass the  class. This class should be avoided in new code, as it does not provide any advantage, and could actually be confusing (T86704).

When creating a new exception class, consider implementing  if the exception message contains variable parts, and   if the exception message is shown to users.

<span id="See_also">