Vývojáři PHP se po dlouhých diskuzích nakonec rozhodli do pětkové verze PHP dát poměrně důležité věci, které se původně plánovaly až do PHP 6. Vzhledem k tomu, že jde o poměrně zásadní změny a verze PHP 5.3, která je bude obsahovat, bude vypuštěna někdy začátkem roku 2008, je pomalu na čase se s nimy seznámit. Mezi největší změny patří podpora jmenných prostorů (namespaces), late static binding a nové mysql rozhraní mysqlnd. V tomto a několika dalších článcích se pokusím popsat v čem tyto změny spočívají.
Z dnešního pohledu je článek zastaralý, a nezohledňuje implementaci jmenných prostorů v aktuální verzi PHP, byl vydán jako přehled v době, kdy se implementace jmenných prostorů teprve připravovala.
Namespaces - základy
Po podpoře jmenných prostorů (dále jen namespaces, je to kratší 8-)) se v PHP komunitě dlouho volalo.
Hlavní motivací k jejich zavedení je separace symbolů a tím i
jednodušší použití a vytváření knihoven, zejména knihoven objektového kódu.
V současnosti se to obchází různými berličkami v podobě podtržítek apod. (viz. třeba v Zend
Frameworku) vznikají
pak třídy jako Zend_Search_Lucene_Analysis_Analyzer
_Common_Text_CaseInsensitive
.
Jejich používáním se pak kód dost znepřehledňuje. Použití namespaces nám umožní psát
přehledný objektový kód bez obav před kolizemi v názvech. Jedná se tedy o podobnou motivaci
jako v C++, Javě a jiných jazycích, které namespaces používají.
Základní vlastnost namespaces je tedy v tom, že rozdílné namespaces mohou obsahovat stejné názvy tříd, metod, funkcí a konstant. Definice namespace je jednoduchá a měla by se uvádět na začátku souboru.
<?php
/** classes/my/foo/MyClass.php */
namespace my::foo;
//definice tridy
class MyClass {}
//Definice funkci a konstant uvnitr namespace mimo tridu je take mozna
function myFunc() { }
const MY_CONST = 'foo';
?>
Následující kód ukazuje některé možnosti použití věcí definovaných v předchozím namespace.
<?php
/** test.php */
include('classes/my/foo/MyClass.php');
//Pouziti tridy pomoci uplne definice.
$foo = new my::foo::MyClass();
//pomoci klicoveho slova use imporuje definice z namespace my::foo.
use my::foo;
//to nam umozni pouziti kratsich zapisu - misto my::foo jen foo.
$foo = new foo::MyClass();
//muzeme si importovat jen nazev jedne tridy a pak jej pouzivat samostatne.
use my::foo::MyClass;
$foo = new MyClass;
//Muzeme vytvaret aliasy.
use my::foo as MyFoo;
use my::foo::MyClass as MyFooClass;
$foo = new MyFoo::MyClass();
$foo = new MyFooClass();
//Nasledujici vyrazy jsou si ekvivalentni
use my::foo;
use my::foo as foo;
//pristupovat k funcim a konstantam pak muzeme obema zpusoby
my::foo::myFunc();
myFoo::myFunc();
my::foo::MY_CONST;
myFoo::MY_CONST;
?>
Jen namespaces a třídy mohou být importovány pomocí use
. Není možné
importovat třeba konstanty nebo funkce. Use
má platnost od své
deklarace až do konce souboru a můžeme ho použít kdekoliv v globálním namespace.
Můžeme použít stejný namespace ve více souborech, definice namespace by se
však měla v jednom souboru nacházet jen jednou. Je možné, že se na výše
popsaném chování ještě něco změní, např. místo klíčového slova use se mělo původně používat import,
ke změně došlo nedávno.
Globální namespace - ::
Použitím ::
před symbolem říkáme, že se jedná o globálně definovaný symbol
nezávislý od aktuálního namespace importu. Používat se bude zejména při práci uvnitř namespace.
Přechod na použití namespaces - změny ve stávajícím kódu.
Pokud budete chtít začít namespaces používat ve svých projektech a knihovnách, je třeba si dát pozor na některé věci, které použití namespaces přináší. Jde zejména o autoloading, pravidla pro dohledání symbolů uvnitř namespace a pojmenování tříd uvniř namespace.
Jména tříd nesmí kolidovat s globálními klíčovými slovy PHP, pokud tedy dříve bylo
/** classes/my/form/element/static.php */
class MyFormElementStatic {}
nově s namespaces není možné napsat
/** classes/my/form/element/static.php */
namespace my::form::element;
class Static {}
Static
je v PHP klíčové slovo rezervované pro interní použití.
Autoloading
V PHP 5.3 bude funkce __autoload
dostávat jméno třídy plně
kvalifikované včetně namespace. Budete si muset tedy své class loadery trochu modifikovat.
/** test.php */
function __autoload($className)
{
require 'classes/'.str_replace('::', DIRECTORY_SEPARATOR, $className).'.php';
}
$foo = new my::foo::MyClass();
Stejně tak pokud využíváte pro autoloading SPL
/** classes/my/core/classloader.php */
namespace my::core;
function classLoader($className)
{
require 'classes/'.str_replace('::', DIRECTORY_SEPARATOR, $className).'.php';
}
spl_autoload_register('my::core::classLoader');
/** test.php */
require 'classes/my/core/classLoader.php';
$foo = new my::foo::MyClass();
Funkce get_class(), get_parent_class()
budou vracet
plně kvalifikovaná jména včetně namespace.
Reflection API
Podpora namespaces se přidá i do reflection API. Zatím to vypadá asi takto:
- nová třída
ReflectionNamespace
s metodamigetName(), getClasses(), getFunctions(), getFiles()
- třídy
ReflectionClass
andReflectionFunction
se rozšíří o metodugetNamespace()
Přidá se nová konstanta __NAMESPACE__
, která bude obsahovat
jméno aktuálního namespace.
Pro rozhodování, jak se který symbol odkud použije, budou platit docela složitá
pravidla popsaná zatím v
PHP namespaces README. Nejdůležitější je si pamatovat, že PHP se při rozhodování,
který symbol použije, podívá nejdříve do aktuálního namespace.
Příklad:
namespace my::foo;
...
bar();
...
::bar();
Při volání bar(), se nejdříve bude hledat funkce bar() v namespace my::foo. Pak se PHP pokusí zavolat interní (POZOR nikoli definovanou někde globálně ve skriptu) funkci bar(). To znamená, že tímto způsobem nelze volat globálně definované funkce, to lze pak pomocí ::bar(), kde se PHP pokusí zavolat globálně definovanou funkci bar(). A aby toho nebylo málo, tak pro:
namespace my::foo;
core::bar();
::core::bar();
Při core::bar(), se nejdříve zavolá v funkce namespace my::foo::core(), pokud ta neexistuje, PHP se pokusí zavolat metodu bar() interní PHP třídy core. Při ::core::bar(), se PHP pokusí zavolat funkci bar() z namespace core, pokud ta neexistuje tak metodu bar() globálně definované třídy core.
Podpora namespaces je určitě dobrým krokem, přinese standardní prostředek pro psaní a práci s kódem objektových knihoven. Otázkou je, jak rychle a jestli vůbec dojde k adopci namespaces ve stávajících frameworcích, zejména Zend Frameworku, uvidíme. Osobně jsem docela pesimista, jsou ale už i výjimky, projekt Phing, o kterém jsem se zmínil nedávno v článku o deploymentu PHP aplikací už na namespaces přešel. Stejně tak nová verze balíčkovacího systému PEAR bude namespaces používat.
Komentáře (5)
v ZF se podle mě namespace určitě nevyskytnou kvůli zpětný kompatibilitě
osobně se mi ale moc namespaces, tak jak jsou teď moc nelíbí
už jenom kvůli tomu, že nemůžu definovat v jednom souboru víc namespaců(nechápu proč není rozsah namespace v blocích pomocí {})
navíc když udělám:
namespace my::foo;
class MyClass {}
a pak v dalším souboru:
use my::foo;
$foo = new foo::MyClass();
nechápu proč musím znovu spát foo když už ho mám jednou v use
sice je tohle lepší jak drátem do oka ale nevidím v tom zatím moc výhod :-/
podle mě se měli třeba inspirovat C#
Taky myslím, že v ZF to v nejbližší době nebude, při změně major verze (ZF 2.0) by se ale přechod na namespace mohl uskutečnit.
S tím
use my::foo;
$foo = new foo::MyClass();
je to pravda, asi to má nějakou souvislost s tím rozhodováním, co se odkud použije.
se vsadim ze to zmasti.
Ahoj nic proti ale namespace se dá také definovat s {} takže třeba takhle:
namespace Namespace1
{
class Trida 1
{}.........
}
namespace Namespace 2
{
........
}
Ale více namespaců jde používat také bez {} takže se napíše:
namespace Namespace1;
class Trida1
{
}
.
.
.
.
namespace Namespace2;
.
.
.
.
.
namespace Namespace3;
.
.
.
.
Takže nevím kde jste to vzali, že nelze použít více namespaces.
No a využití namespace spočívá hlavně v tom, že když používáte nějaké frameworky tak mohou mít stejné třídy, jaké používáte vy ve svém projektu takže vy si dáte ke svému projektu nějaké namespace (já teď dělám třeba "wapi") a už se vám nebudou třídy a metody kolidovat s třídami z onoho frameworku protože je budete volat pomocí wapi::Trida1
[6] - jasně, tenhle článek je už zastaralý, je z roku 2007, kdy se teprve řešilo, jak na to, někam to tam napíšu
Komentáře jsou uzavřeny.