Manuál:Konvence pro psaní kódu/PHP

From mediawiki.org
Jump to navigation Jump to search
This page is a translated version of the page Manual:Coding conventions/PHP and the translation is 84% complete.
Outdated translations are marked like this.
Other languages:
English • ‎dansk • ‎español • ‎français • ‎čeština • ‎русский • ‎中文 • ‎日本語
shortcut: CC/PHP

Tato stránka popisuje konvenci která se používá při psaní u PHP skriptů u MediaWiki . See also the general conventions that apply to all program languages, including PHP. If you would like a short checklist to help you review your commits, try using the Pre-commit checklist .

Aby mohli vývojáři snadno odhalit kód, který není v souladu s touto konvencí, existuje nástroj stylize, který kontroluje syntaxi a doplní mezery na příslušná místa. Tento nástroj je dostupný přes webové rozhraní. It is recommended to run this over files committed to Git.

Jsou také k dispozici pravidla pro PHP_CodeSniffer. For more information, see Continuous integration/PHP CodeSniffer . The git repository https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/tools/codesniffer contains the PHP CodeSniffer settings for MediaWiki code.

Struktura kódu

Mezery

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

Odsazení s tabulátory, nikoli mezerami. Omezte řádky na 120 znaků (vzhledem k šířce karty 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:

// No:
$a=$b+$c;

// Yes:
$a = $b + $c;

Také obsah závorek oddělujte mezerami. Výjimkou je pouze situace, kdy závorky nemají žádný obsah. Naopak mezi jméno funkce a otevírací závorku pro její obsah mezeru nedávejte.

$a = getFoo( $b );
$c = getBar();

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.

// Yes
$a = [ 'foo', 'bar' ];
$c = $a[0];
$x = [];

// No
$a = ['foo', 'bar'];
$c = $a[ 0 ];
$x = [ ];

Za řídící prvky, jako je if, while, for, foreach, switch nebo klíčové slovo catch naopak mezeru vložte vždy:

// Yes
if ( isFoo() ) {
	$a = 'foo';
}

// No
if( isFoo() ) {
	$a = 'foo';
}

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

// Yes
(int)$foo;

// No
(int) $bar;
( int )$bar;
( int ) $bar;

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

// Yes: Proper inline comment
//No: Missing space
/***** Do not comment like this ***/

Jako vývojářskou pomůcku, která se postará o správné umístění mezer, lze použít nástroj stylize, který přidává mezery automaticky podle konvence PHP. Nástroj je dostupný přes webové rozhraní.

Ternární operátor

ternary operator může být velice užitečný, pokud jsou výrazy velmi krátké a zřejmé:

$title = $page ? $page->getTitle() : Title::newMainPage();

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 if (). Remember, disk space is cheap, code readability is everything, "if" is English and "?:" is not. If you are using a multi-line ternary expression, the question mark and colon should go at the beginning of the second and third lines and not the end of the first and second (in contrast to MediaWiki's JavaScript convention).

Protože MediaWiki vyžaduje PHP 7.3.19 (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 a v některých případech může nahradit ternárního operátora. Například místo

$wiki = isset( $this->mParams['wiki'] ) ? $this->mParams['wiki'] : false;

místo toho můžete napsat následující:

$wiki = $this->mParams['wiki'] ?? false;

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 escape sequences nebo variables. For example, the regular expression "/\\n+/" requires an extra backslash, making it slightly more confusing and error-prone than '/\n+/'. Also for people using US/UK qwerty keyboards, they are easier to type, since it avoids the need to press shift.

Nebojte se však používat dvojitě citovanou funkci interpolace řetězců PHP: $elementId = "myextension-$index"; To má o něco lepší výkonové vlastnosti než ekvivalent pomocí operátoru zřetězení (tečka) a vypadá to také hezčí.

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

$s = <<<EOT
<div class="mw-some-class">
$boxContents
</div>
EOT;

Někteří autoři rádi používají END jako koncový token, který je také názvem funkce PHP. To vede k IRC konverzacím, jako jsou následující:

Funkce a jejich parametry

Vyhněte se předávání obrovského počtu parametrů funkcím nebo konstrukcím:

// Constructor for Block.php from 1.17 to 1.26. *DON'T* do this!
function __construct( $address = '', $user = 0, $by = 0, $reason = '',
	$timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
	$hideName = 0, $blockEmail = 0, $allowUsertalk = 0
) {
	...
}

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 jste v pokušení kódovat funkci jako je tato, zvažte raději předání asociativní sady pojmenovaných parametrů.

Obecně se nedoporučuje ve funkcích používání booleovských parametrů. In $object->getSomething( $input, true, true, false ), without looking up the documentation for MyClass::getSomething(), it is impossible to know what those parameters are meant to indicate. Much better is to either use class constants, and make a generic flag parameter:

$myResult = MyClass::getSomething( $input, MyClass::FROM_DB | MyClass::PUBLIC_ONLY );

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

$myResult = MyClass::getSomething( $input, [ 'fromDB', 'publicOnly' ] );

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ě celý bod 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:

if ( $a = foo() ) {
    bar();
}

Prostor je levný a vy jste rychlý psací stroj. Místo toho použijte:

$a = foo();
if ( $a ) {
    bar();
}

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

$res = $dbr->query( 'SELECT * FROM some_table' );
while ( $row = $dbr->fetchObject( $res ) ) {
    showRow( $row );
}

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

$res = $dbr->query( 'SELECT * FROM some_table' );
foreach ( $res as $row ) {
    showRow( $row );
}

Výpůjčky z jazyka C

PHP je programovací jazyk, který byl navržen lidmi, kteří milovali programování v 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.

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

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

// Tento kód:
if ( $foo == 'bar' ) {
	echo 'Hello world';
} else if ( $foo == 'Bar' ) {
	echo 'Hello world';
} else if ( $baz == $foo ) {
	echo 'Hello baz';
} else {
	echo 'Eh?';
}

// Bude mít v zásadě stejný efekt, jako tento kód:
if ( $foo == 'bar' ) {
	echo 'Hello world';
} else {
	if ( $foo == 'Bar' ) {
		echo 'Hello world';
	} else {
		if ( $baz == $foo ) {
			echo 'Hello baz';
		} else {
			echo 'Eh?';
		}
	}
}

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 endif, endwhile, atp., používat alternativní syntaxi, při které se místo složených závorek použije dvojtečka:

if ( $foo == $bar ):
    echo "<div>Hello world</div>";
endif;

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

if ( $foo == $bar ) {
    echo "<div>Hello world</div>";
}

Závorky

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

Deklarování typů v parametrech funkce

Používejte vstupní deklaraci typu (podívejte se na naznačení typu – type hinting) a deklarujte i typ návratové hodnoty, všude kde to jen lze.

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

public function foo ( ?MyClass $mc ) {}

instead of

public function foo ( MyClass $mc = null ) {}

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 lower-CamelCase notaci. Jak demonstruje ukázkový příklad:

private function doSomething( $userPrefs, $editSummary )

Pro názvy tříd používejte naopak Upper-CamelCase: class ImportantClass. Použijte velká písmena s podtržítky pro globální a třídní konstanty: DB_MASTER, Revision::REV_DELETED_TEXT. Jiné proměnné jsou obvykle malá nebo nižšíCamelCase; nepoužívejte podtržítka v názvech proměnných.

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

Funkce

  • wf (wiki funkce) – funkce, dostupné na té nejvyšší úrovni, např.
    function wfFuncname() { ... }
    
  • ef (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#ef_prefix_9510)

V tomto případě se preferují slovesa, která jasně naznačí co má funkce udělat: takže místo getReturnText() použijte returnText().

Proměnné

  • $wg – globální proměnné, např. $wgTitle. Toto vždy používejte pro nové globály, abyste mohli snadno najít chybějící prohlášení „global $wgFoo". V rozšířeních by měl být název přípony použit jako oddělovač oboru názvů. Například $wgAbuseFilterConditionLimit a ne $wgConditionLimit.
  • 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 Database. 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 Wikimedia 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.

  • $dbw – objekt Database pro zápis (hlavní připojení)
  • $dbr – objekt Database pro čtení, které není citlivé na souběžnost (může to být replika pouze pro čtení, mírně za hlavní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 mohou být viděny následující, ale v novém kódu jsou odrazovány:

  • $ws – Proměnné relace, např. $_SESSION['wsSessionName']
  • $wc – Proměnné souborů cookie, např. $_COOKIE['wcCookieName']
  • $wp – Proměnné příspěvku (odeslané prostřednictvím polí formuláře), např. $wgRequest->getText( 'wpLoginName' )
  • $m – proměnné členů objektu: $this->mPage. To je v novém kódu odrazováno, ale zkuste zůstat v rámci třídy konzistentní.

Úskalí

empty()

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

  • empty( $var ) essentially does !isset( $var ) || !$var.
    Obecný případ použití: Volitelné booleovské konfigurační klíče, které mají výchozí hodnotu false. $this->enableFoo = !empty( $options['foo'] );
  • Dejte si pozor na booleovské konverze.
  • Potlačuje chyby ohledně nedefinovaných vlastností a proměnných. Pokud chcete testovat pouze nedefinované, použijte !isset(). Pokud máte v úmyslu pouze otestovat „prázdné“ hodnoty (např. False, nula, prázdné pole, atd.), Použijte !.

isset()

Nepoužívejte isset() k testování null. Using isset in this situation could introduce errors by hiding misspelled variable names. Instead, use $var === null.

Booleovská konverze

if ( !$var ) {
    
}
  • Nepoužívejte ! nebo empty k testování, zda je řetězec nebo pole prázdné, protože PHP považuje '0' za falešné - ale '0' je platný název a platné uživatelské jméno na MediaWiki. Místo toho použijte === '' nebo === [].
  • Prostudujte si pravidla pro převod conversion to boolean. 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 [ 'a' ] + [ 'b' ] === [ 'a' ]. Pokud chcete, aby klíče byly přečíslovány, použijte array_merge(): array_merge( [ 'a' ], [ 'b' ] ) == [ 'a', 'b' ]
  • Ujistěte se, že jste error_reporting() nastavili na -1. Toto vás upozorní na nedefinované proměnné a další jemné gotchas, které bude PHP PHP ignorovat. Viz též Návod: Jak ladit .
  • 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“ (srovnání viz. Bugzilla: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 goto() zavedenou v 5.3. PHP možná zavedlo tuto funkci, ale to neznamená, že bychom ji měli používat.
  • Pokud projíždíte maticí, pokud nebudete muset, neprocházejte referencí. I přesto si uvědomte důsledky. (Viz http://www.king-foo.be/2010/10/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 private static, nepoužívejte ani statickou proměnnou.

Operátoři rovnosti

Buďte opatrní s double-equals comparison operators. Triple-rovná se (===) je obecně intuitivnější a měla by být upřednostňována, pokud nemáte důvod k použití dvojitých equals (==).

  • 'foo' == 0 je pravda (!)
  • '000' == '0' je pravda (!)
  • '000' === '0' je nepravdivý
  • Chcete-li zkontrolovat, zda jsou dva skaláry, které mají být číselné, stejné, použijte ==, např. 5 == "5" je pravda.
  • Chcete-li zkontrolovat, zda jsou dvě proměnné typu „řetězec“ a jsou stejné posloupnosti znaků, použijte ===, například "1.e6" === "1.0e6" je nepravda.

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 64bit IEEE floating point numbers. 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, představujte potenciálně velká celá čísla jako řetězce v JSON.

Dos and Don'ts

Nepoužívejte vestavěnou serializaci

Integrovaný mechanismus serializace PHP (funkce serialize() a unserialize()) 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 je politika 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á 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é členů 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 Manual:mwdocgen.php). Začněte blok Doxygenových komentářů pomocí /**, namísto formátování ve formátu Qt /*!. Strukturální příkazy doxygen začínají @tagname. (Jako únikový znak použijte spíše @ než \ - oba styly fungují v Doxygenu, ale pro zpětnou a budoucí kompatibilitu si MediaWiki zvolila @param styl.) Organizují vygenerovanou dokumentaci (pomocí @ingroup) a identifikují autory (pomocí značek @author).

Používají se při popisu parametrů, které se předávají funkci (metodě) (@param), a i toho co funkce vrací (@return). Při popisu parametrů to vypadá takto:

@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:

@param string|Language|bool $lang Language for the ToC title, defaults to user language

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 @since VERSION, 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.

class Foo {

	/**
	 * @var array Description here
	 * @example [ 'foo' => Bar, 'quux' => Bar, .. ]
	 */
	protected $bar;

	/**
	 * Description here, following by documentation of the parameters.
	 *
	 * Some example:
	 * @code
	 * ...
	 * @endcode
	 *
	 * @since 1.24
	 * @param FooContext $context context for decoding Foos
	 * @param array|string $options Optionally pass extra options. Either a string
	 *  or an array of strings.
	 * @return Foo|null New instance of Foo or null of quuxification failed.
	 */
	public function makeQuuxificatedFoo( FooContext $context = null, $options = [] ) {
		/* .. */
	}

}

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).

<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @file
 */

Značky pro Doxygen

V našich zdrojácích 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
  • @ingroup
  • @author

Třídy a jejich vlastnosti:

  • @todo
  • @var
  • @deprecated
  • @class
  • @see
  • @since
  • @param
  • @return
  • @throws
  • @private

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
  • @expectedException
  • @expectedExceptionMessage


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 /includes/libs). 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!

Globální objekty

Nepřistupujte k superglobálním proměnným PHP jako $_GET, $_POST, atp. přímo. Získávejte jejich hodnotu raději přes volání metody $request->get*( 'param' ). 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í WebRequest můžete zjistit z nejbližšího objektu RequestContext, takže není vůbec nutné kvůli tomu volat RequestContext::getMain(). Equally, do not access $_SERVER directly; use $request->getIP() if you want to get the IP address of the current user.

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 self a static. self bude vždy odkazovat na funkce a proměnné v rámci třídy, zatímco static bude odkazovat na konkrétní podtřídu, která ji zavolá. Pro víc podrobnosti si přečtěte v dokumentaci k PHP o dědičnosti a pozdní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 User, Block, RevisionRecord, aj. A třídami co obsluhují frontend, jako SpecialPage, Article, ChangesList a jiné, co zastupují stránky a rozhraní co se vrací uživateli. Pokud není váš kód zrovna objektově orientovaný, je lepší ho umístit alespoň do statické třídy, jako jsou např. třídy IP nebo Html. Try to be mindful of the distinction between 'backend' classes, which represent entities in the database (e.g. User, Block, RevisionRecord, etc), and 'frontend' classes, which represent pages or interfaces visible to the user (SpecialPage, Article, ChangesList, etc. Even if your code is not obviously object-oriented, you can put it in a static class (e.g. IP or Html).

Vzhledem k tomu, že PHP 4 má nedostatek členů a metod soukromé třídy, bude starší kód označen komentáři, jako je /** @private */, což naznačuje záměr; respektujte to, jako by to bylo vynuceno tlumočníkem.

Označte nový kód správnými modifikátory viditelnosti, včetně public, 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. It's generally a good idea to avoid visibility changes unless you're making changes to the function which would break old uses of it anyway.

Jak ošetřit chyby

In general, you should not suppress PHP errors. The proper method of handling errors is to actually handle the errors.

For example, if you are thinking of using an error suppression operator to suppress an invalid array index warning, you should instead perform an isset check on the array index before trying to access it. When possible, always catch or naturally prevent PHP errors.

Only if there is a situation where you are expecting an unavoidable PHP warning, you may use PHP's @ operator. This is for cases where:

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

We use PHPCS to warn against use of the at-operator. If you really need to use it, you'll also need to instruct PHPCS to make an exemption, like so:

// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
$content = @file_get_contents( $path );

An example use case is opening a file with fopen(). You can try to predict the error by calling file_exists() and is_readable(), but unlike isset(), such file operations add significant overhead and make for unstable code. For example, the file may be deleted or changed between the check and the actual fopen() call (see TOC/TOU).

In this case, write the code to just try the main operation you need to do. Then handle the case of the file failing to open, by using the @ operator to prevent PHP from being noisy, and then check the result afterwards. For fopen() and filemtime(), that means checking for a boolean false return, and then performing a fallback, or throw an exception.

Exceptions that indicate programming errors should be one of the exceptions that ship with PHP or a more specific subclass, while exceptions that indicate errors that are relevant to the end user should be an ErrorPageError or one of its subclasses.

AtEase

For PHP 5 and earlier, MediaWiki developers discouraged use of the @ operator due to it causing unlogged and unexplained fatal errors (r39789). Instead, we used custom AtEase::suppressWarnings() and AtEase::restoreWarnings() methods from the at-ease library. The reason is that the at-operator caused PHP to not provide error messages or stack traces upon fatal errors. While the at-operastor is mainly intended for non-fatal errors (not exceptions or fatals), if a fatal were to happen it would make for a very poor developer experience.

use Wikimedia\AtEase\AtEase;

AtEase::suppressWarnings();
$content = file_get_contents( $path );
AtEase::restoreWarnings();

In PHP 7, the exception handler was fixed (example) to always provide such errors, including a stack trace, regardless of error suppression. In 2020, use of AtEase started a phase out, reinstating the at-operator. (T253461)

Viz též