Mimo, że do premiery pozostało jeszcze wiele czasu, warto już teraz zapoznać się ze zmianami jakie wprowadzi PHP6. Większość z nich znajdziemy także w PHP5.3 (planowana data wydania: trzeci kwartał 2008).
Wszystkie przedstawione w artykule przykłady zostały przetestowane na PHP6 skompilowanym ze źródeł z CVS.
Wsparcie dla Unicode w PHP6 jest jedną z nowości, które na pewno nie pojawią się w PHP5.3.
Głównym założeniem wsparcia Unicode jest pełna kompatybilność wsteczna. Wszystkie istniejące typy danych i funkcje działają identycznie jak we wcześniejszych wersjach, aczkolwiek możliwe jest obniżenie wydajności niektórych operacji spowodowane zmianami w ich implementacji.
Fallback Encoding jest dyrektywą konfigurującą domyślne wartości dla wszystkich opcji konfiguracyjnych z rodziny unicode.*_encoding.
unicode.fallback_encoding = "iso-8859-2"
Domyślna wartość dla tej opcji konfiguracyjnej to "UTF-8".
Runtime Encoding kontroluje kodowanie używane wewnętrznie przez interpreter do konwersji łańcuchów binarnych (typ danych binary).
unicode.runtime_encoding = "iso-8859-2"
Ta opcja konfiguracyjna nie wpływa na operacje wejścia/wyjścia (takie jak: wypisywanie na wyjście czy czytanie z systemu plików).
PHP umożliwia także jawne rzutowanie łańcuchów tekstowych:
Na przykład, jeśli unicode.runtime_encoding='iso-8859-2' a $uni jest łańcuch tekstowy z obsługą Unicode, wtedy
$str = (binary)$uni;
stworzy łańcuch binarny $str w kodowaniu ISO-8859-2
Niejawne rzutowanie zachodzi także m.in. podczas konkatencji czy porównania. Jeśli próbujemy połączyć łańcuch binarny ($str) z literałem Unicode, PHP konwertuje $str na Unicode używając kodowania podanego jako unicode.runtime_encoding, a następnie je łączy.
Podczas wypisywania danych na standardowe wyjście PHP automatycznie dokonuje konwersji łańcuchów tekstowych z obsługą Unicode na kodowanie podane w dyrektywie:
unicode.output_encoding = "utf-8"
Ta dyrektywa kontroluje kodowanie używane w nazwach plików i katalogów.
unicode.filename_encoding = "utf-8"
Wszystkie funkcje operujące na systemie plików (takie jak opendir) dokonują niejawnej konwersji otrzymanych oraz zwracanych nazw plików i katalogów, dlatego ważne jest ustawienie wartości dyrektywy na kodowanie używane przez system plików.
Jak wspomniałem wcześniej istnieją dwa typy danych obsługujące łańcuchy znaków: binary i unicode string.
Ten typ danych przechowuje tekst używając kodowania UTF-16. Jest on domyślnym typem dla łańcuchów tekstowych.
Łańcuchy binarne służą przede wszystkim:
Wypisywanie danych binarnych na standardowe wejście nie powoduje automatycznej konwersji kodowania, tak jak ma to miejsce w przypadku typu unicode.
Głównym założeniem obecnej implementacji przestrzeni nazw w PHP jest rozwiązanie problemu bardzo długich nazw klas.
Przestrzeń nazw deklarujemy w następujący sposób:
<?php namespace Zend::DB; class Connection { } function connect() { } ?>
Do deklaracji przestrzeni nazw używamy słowa kluczowego namespace po którym następuje identyfikator przestrzeni nazw, którego kolejne człony mogą być oddzielone :: (podwójnym dwukropkiem). Umieszczenie deklaracji na samym początku pliku (jedynie po dyrektywie declare lub komentarzu) spowoduje, że wszystkie identyfikatory klas i funkcji zdefiniowanych wewnątrz przestrzeni nazw będą automatycznie poprzedzane prefiksem przestrzeni nazw.
Możliwe jest także zadeklarowanie kilku przestrzeni nazw w pliku:
<?php namespace FirstNamespace;class Foo{} echo __NAMESPACE__.PHP_EOL;namespace SecondNamespace; echo __NAMESPACE__.PHP_EOL;class Foo{} ?>
Przestrzenie nazw oraz klasy mogą zostać zaimportowane:
<?php require 'Zend/Db/Connection.php'; use Zend::DB; use Zend::DB::Connection as DbConnection;$x = new Zend::DB::Connection(); $y = new DB::connection(); $z = new DbConnection(); DB::connect(); ?>
Użyte w powyższym przykładzie, słowo kluczowe use tworzy jedynie alias nazwy (klasy lub przestrzeni nazw). Prostsza forma:
use A::B::C::D;
jest równoznaczna z
use A::B::C::D as D;
Tworzenie aliasów możliwe jest w każdym miejscu pliku (poza ciałami klas i funkcji) - działa od miejsca deklaracji do końca pliku, w którym się znajduje. Dla zachowania czytelności kodu zaleca się umieszczanie dyrektyw use na początku pliku. Możliwe jest także importowanie wielu symboli przy pomocy pojedyńczej dyrektywy use:
use A::B as B, D as E;
Nazwy klas i funkcji poprzedzone podwójnym dwukropkiem traktowane są jako bezpośrednie odwołania do klas i funkcji zdefiniowanej w domyślnej przestrzeni nazw, w której znajdują się obecnie wszystkie funkcje i klasy wbudowane oraz nieprzypisane do konkretnej przestrzeni nazw.
<?php namespace A::B::C; $con = ::mysql_connect(...); ?>
Autoloader jest wywoływany, gdy poszukiwanej klasy nie ma w obecnej oraz domyślnej przestrzeni nazw, a jako parametr otrzymuje pełną kwalfikowaną nazwę klasy.
Funkcja __autoload zadeklarowana wewnątrz przestrzeni nazw nie będzie automatycznie wywoływana, należy ją zarejestrować za pomocą spl_autoload_register.
Stałe __CLASS__ i __FUNCTION__ zwracają pełne nazwy kwalfikowane (wraz z informacją o przestrzeni nazw). Natomiast stała __NAMESPACE__ zwraca przestrzeń nazw, w której została umieszczona.
get_class() - zwraca pełną nazwę kwalfikowaną klasy
LSB jest rozwiązaniem następującego problemu:
<?php class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } ?>
Statyczne referencje do klasy ( czyli self:: i __CLASS__) zawsze zwracają odwołania do klasy, w której zostały zadeklarowane.
LSB wprowadza możliwość uzyskania referencji do klasy, która została wywołana (poprzez konstrukcję static::):
<?php class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); ?>
Powyższy kod można zastąpić krótszym, działającym identycznie, korzystając z funkcji get_called_class zwracającej nazwę ostatnio wywoływanej klasy:
<?php class A { public static function test() { echo get_called_class(); } } class B extends A { } B::test(); ?>
Do PHP zostały dodane także odpowiedniki funkcji call_user_func i call_user_func_array wykorzystujące LSB: forward_static_call i forward_static_call_array.
Wykorzystajmy teraz LSB do stworzenia klasy bazowej dla Singletonów:
<?php class Singleton { static $instances = array(); public static function factory() { $class = get_called_class(); if ( !isset( self::$instances[ $class ] ) ) { self::$instances[ $class ] = new static(); } return self::$instances[ $class ]; } } class foo extends Singleton{} class bar extends Singleton{} $foo1 = foo::factory(); $foo2 = foo::factory(); var_dump($foo1); var_dump($foo2); var_dump($foo1===$foo2); ?>
Metoda factory() przedstawiona na powyższym listingu pobiera najpierw nazwę wywołanej klasy, a następnie działa analogicznie do standardowej implementacji Singletona.
Dopiero w PHP6 metoda przesłaniająca w klasie potomnej może zyskać nowy parametr z wartością domyślną na końcu listy argumentów, albo jeden z jej argumentów może zyskać wartość domyślną.
<?php abstract class Base { abstract function someMethod($param); abstract function anotherMethod($param); } class Ext extends Base { function someMethod($param='default') { echo $param, "\n"; } function anotherMethod($param='default', $newParam = 'default') { echo $param, "\n"; } } $a = new Ext(); $a->someMethod("foo"); $a->someMethod(); ?>
Składnia NOWDOC jest kolejnym sposobem na zapisanie łańcucha znaków, zachowującym się jak tekst w cudzysłowach pojedynczych.
Przykład użycia
<?php $fooledYou = ''; print <<<'ENDOFNOWDOC' {$fooledYou}ENDOFNOWDOC{$fooledYou} ENDOFNOWDOC{$fooledYou} {$fooledYou}ENDOFNOWDOC ENDOFNOWDOC; ?>
Wynik działania skryptu.
{$fooledYou}ENDOFNOWDOC{$fooledYou} ENDOFNOWDOC{$fooledYou} {$fooledYou}ENDOFNOWDOC
Instrukcja goto pozwala na bezwarunkowe przekazanie sterowania do tzw. etykiety. Etykietę deklarujemy podając jej nazwę, a następnie dwukropek, np. etykieta: . Instrukcja goto pozwala na skok jedynie pod warunkiem, że przekazanie sterowania nie nastąpi do wnętrza bardziej zagnieżdżonego bloku kodu, np.:
Niepoprawne wykorzystanie operatora goto
<?php goto L1; while (0) { L1: echo "bug\n"; } ?>
Poprawne wykorzystanie operatora goto
<?php $n = 1; L1: if ($n > 3) goto L2; echo "$n: ok\n"; $n++; goto L1; L2: ?>
Dodano wsparcie dla przekazywania nazwy klasy w postaci zmiennej przy odwołaniu do składowej statycznej.
<?php class foo { public static function myFunc() { echo "myFunc"; } } $foo = 'foo'; $foo::myFunc(); ?>
Powyższy przykład wyświetli myFunc.
Wprowadzono konstrukcję analogiczną do __call działającą dla metod statycznych.
Przykład użycia __callStatic()
<?php class Test { static function __callStatic($fname, $args) { echo $fname,'() called with ',count($args)," arguments\n"; } } call_user_func("Test::Two", 'A', 'B'); call_user_func(array("Test", "Three"), NULL, 0, false); Test::Four(5, 6, 7, 8); ?>
Operator ?: pozwala w bardzo prosty sposób przypisywać wartości domyślne dla niezainicjalizowanych zmiennych. Poniższe przykłady działają identycznie
<?php $a = $b?:$c; if ( $b ) { $a = $b; } else { $a = $c; } ?>
Należy pamiętać, że oba powyższe przykłady generują ostrzeżenie E_NOTICE, co znacznie zmniejsza atrakcyjność przedstawionego rozwiązania.
Opcjonalny argument break i continue może być jedynie stałą.
Poprawne zastosowanie break
<?php while(true){ while(true){ break 2; } } ?>
Niepoprawne zastosowanie break
<?php $a=2; while(true){ while(true){ break $a; } } ?>
Powyższy kod wygeneruje błąd E_FATAL_ERROR.
instanceof, catch, is_a, is_subclass_of nie wywołują automatycznie funkcji __autoload.
Rozszerzenie SPL wzbogaciło się o kilka nowych klas implementujących struktury danych:
<?php $dll = new SplDoublyLinkedList(); $dll->push(2); $dll->push(3); $dll->push(4); $dll->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO); foreach ($dll as $k => $v) { echo "$k=>$v\n"; } $dll->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO); foreach ($dll as $k => $v) { echo "$k=>$v\n"; } $dll->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_DELETE); var_dump($dll->count()); foreach ($dll as $k => $v) { echo "$k=>$v\n"; } var_dump($dll->count()); ?>
Wynik działania skryptu:
2=>4 1=>3 0=>2 0=>2 1=>3 2=>4 int(3) 0=>2 1=>3 2=>4 int(0)
<?php $input = range(1,5); shuffle($input); $h = new SplMinHeap(); foreach($input as $i) { $h->insert($i); } foreach ($h as $k => $o) { echo "$o\n"; } ?>
Powyższy kod wypisze:
1 2 3 4 5
<?php $input = range(1,5); shuffle($input); $h = new SplMaxHeap(); foreach($input as $i) { $h->insert($i); } foreach ($h as $k => $o) { echo "$o\n"; } ?>
5 4 3 2 1
<?php $size = 100; $array = new SplFastArray($size); for ( $i=0; $i<$size; ++$i){ $array[$i] = $i; } $array -> setSize(1000); ?>
<? var_dump(iterator_to_array(new GlobIterator('*.php'))); ?>
array(4) { ["/GlobIterator.php"]=> object(SplFileInfo) (2) { [u"pathName":u"SplFileInfo":private]=> string(0) "" [u"fileName":u"SplFileInfo":private]=> string(17) "/GlobIterator.php" } ["/LSB.php"]=> object(SplFileInfo) (2) { [u"pathName":u"SplFileInfo":private]=> string(0) "" [u"fileName":u"SplFileInfo":private]=> string(8) "/LSB.php" } ["/SplMaxHeap.php"]=> object(SplFileInfo) (2) { [u"pathName":u"SplFileInfo":private]=> string(0) "" [u"fileName":u"SplFileInfo":private]=> string(15) "/SplMaxHeap.php" } ["/SplMinHeap.php"]=> object(SplFileInfo) (2) { [u"pathName":u"SplFileInfo":private]=> string(0) "" [u"fileName":u"SplFileInfo":private]=> string(15) "/SplMinHeap.php" } }
<?php namespace foo; class MyClass { } class_alias('foo::MyClass', 'MyAlias'); use ::MyAlias as stdClass; var_dump(new foo::MyClass); var_dump(new stdClass); var_dump(new ::MyAlias); ?>
object(foo::MyClass)1 (0) { } object(foo::MyClass)1 (0) { } object(foo::MyClass)1 (0) { }
<?php print_r(get_extension_funcs("spl")); ?>
Array ( [0] => spl_classes [1] => spl_autoload [2] => spl_autoload_extensions [3] => spl_autoload_register [4] => spl_autoload_unregister [5] => spl_autoload_functions [6] => spl_autoload_call [7] => class_parents [8] => class_implements [9] => spl_object_hash [10] => iterator_to_array [11] => iterator_count [12] => iterator_apply )
<?php $n = gmp_init("1000000"); var_dump(gmp_testbit($n, 1)); gmp_setbit($n, 1); var_dump(gmp_testbit($n, 1)); ?>
bool(false) bool(true)
<?php $csvData = <<<DATA value1;"value 2";"value ""3" DATA; var_dump(str_getcsv($csvData, ';', '"', '"')); ?>
array(3) { [0]=> unicode(6) "value1" [1]=> unicode(7) "value 2" [2]=> unicode(8) "value "3" }
W PHP6 znajdzie się też szereg przydatnych stałych takich jak:
Jani Taskinen poprawił obsługę plików INI w PHP:
;Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" user_ini.filename = ".user.ini" ;To disable this feature set this option to empty value ;user_ini.filename = "" ;TTL for user-defined php.ini files (time-to-live) in seconds. ;Default is 300 seconds (5 minutes) user_ini.cache_ttl = 300
[variable] var = 42 var2 = ${var} [arrays] foo[bar] = 1 foo[${val}] = "variable"
Od PHP5.3 możliwe jest pobieranie wartości właściwości prywatnych i chronionych poprzez Reflection API, wystarczy wywołać metodę ReflectionProperty::setAccessible(true).
<?php class Foo { private $property; public function __construct($property){ $this->property = $property; } } $property = new ReflectionProperty('Foo', 'property'); $property -> setAccessible(true); echo $property -> getValue( new Foo(42) ); ?>
Rozszerzenie to pozwala na dystrybucję aplikacji w postaci pojedyńczego pliku Phar (podobnego do pliku JAR w Javie). Możemy wyróżnić dwa typy plików Phar:
Załóżmy, że mamy prostą aplikację składającą się z jednego pliku index.php:
<?php echo 'Hello Phar!'; ?>
Aby stworzyć z niej wykonywalne archiwum Phar należy wykonać następujący skrypt:
<?php $phar = new Phar('hello.phar'); $phar->setDefaultStub('index.php'); $phar->addFile('index.php'); ?>
Pierwszy argument metody setDefaultStub jest ścieżką do pliku, który ma zostać wykonany po uruchomieniu pliku Phar (nawet w przypadku braku zainstalowanego rozszenia) przy pomocy komendy
php hello.phar
<?php $xml = <<<EOB <allusers> <user> <uid>bob</uid> </user> <user> <uid>joe</uid> </user> </allusers> EOB; $xsl = <<<EOB <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheetversion="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns: php=? http://php.net/xsl ?> <xsl:output method="html" encoding="utf-8" indent="yes" /> <xsl:template match="allusers" ?> <html><body> <h2>Users</h2> <table> <xsl:for-each select="user" ?> <tr><td> <xsl:value-of select="php:function( "ucfirst", string( uid ) ) "/> </td></tr> </xsl:for-each> </table> </body></html> </xsl:template> </xsl:stylesheet> EOB;$xmldoc = DOMDocument::loadXML( $xml ) ;$xsldoc = DOMDocument::loadXML( $xsl ); $proc = new XSLTProcessor; $proc->registerPHPFunctions( ) ; $proc->importStyleSheet( $xsldoc ) ; $proc->setProfiling( "/tmp/xslt.profile" ); echo $proc->transformToXML( $xmldoc ) ; ?>
Rezultaty profilowania wygladaja nastepujaco:
mysqlnd (MySQL Native Driver for PHP) jest biblioteką zastępującą libmysql. Zalety mysqlnd w stosunku do libmysql:
Dotychczas brakuje wsparcia dla PDO_MySQL.
Dodano funkcje:
Dodano stałe:
W PHP6 znajdzie się ulepszony GarbageCollector usuwający referencje cykliczne, pozwalający (w niektórych przypadkach) znacznie zredukować zapotrzebowanie na pamięć przy niewielkim spadku wydajności. GarbageCollector można włączyć poprzez ustawienie opcji (domyślnie włączona):
zend.enable_gc=true
Nowy mechanizm GC jest ekstremalnie skuteczny w sytaucji przedstawionej na poniższym listingu:
<?php class A { function __construct () { $this->b = new B($this); } } class B { function __construct ($parent = NULL) { $this->parent = $parent; } } while (true){ $a = new A(); } ?>
Dodano także kilka funkcji sterujących GC:
Przy poziomie raportowania E_ALL będą wyświetlane także błędy E_STRICT.
Można filtrować błędy E_STRICT używając poniższego kodu:
<?php error_reporting(error_reporting() & ~E_STRICT); ?>
safe_mode został usunięty. Ustawienie dyrektywy safe_mode w php.ini powoduje wyemitowanie błędu E_WARNING.
Nie ma możliwości emulacji zachowania z PHP5.
register_long_arrays oraz tablice $HTTP_* zostały usunięte. PHP emituje E_WARNING podczas uruchomienia, jeśli wykryje włączone register_long_arrays.
<?php if (!ini_get('register_long_arrays')) { $HTTP_POST_VARS =& $_POST; $HTTP_GET_VARS =& $_GET; $HTTP_COOKIE_VARS =& $_COOKIE; $HTTP_SERVER_VARS =& $_SERVER; $HTTP_ENV_VARS =& $_ENV; $HTTP_POST_FILES =& $_FILES; } ?>
Usunięto dyrektywy:
Ustawienie ich spowoduje wyemitowanie ostrzeżenia E_WARNING.
Usunięte funkcje:
Funkcje:
zwracają zawsze false.
Ze względów bezpieczeństwa register_globals zostały usunięte z PHP6.
<?php ini_get("register_globals"); ?>
zawsze zwraca false.
Wraz z usunięciem register_globals straciły rację bytu takie funkcje jak:
Poniższe rozwiązanie zostało zaproponowane przez twórców PHP, powinno być jednak traktowane tylko jako stadium przejściowe i pod żadnym pozorem nie może być używane na stałe.
<?php $_register_globals_order = strrev(ini_get("variables_order")); $_register_globals_order_len = strlen($_register_globals_order); for($_register_globals_i=0; $_register_globals_i<$_register_globals_order_len; $_register_globals_i++) { switch($_register_globals_order{$_register_globals_i}) { case "E": extract($_ENV, EXTR_REFS|EXTR_SKIP); break; case "G": extract($_GET, EXTR_REFS|EXTR_SKIP); break; case "P": extract($_POST, EXTR_REFS|EXTR_SKIP); break; case "C": extract($_COOKIE, EXTR_REFS|EXTR_SKIP); break; case "S": extract($_SERVER, EXTR_REFS|EXTR_SKIP); break; } } unset($_register_globals_order, $_register_globals_order_len, $_register_globals_i); function session_register($mixed) { static $started; if(!isset($started) || session_id() === "") { session_start(); $started = true; } $array = func_get_args(); foreach($array as $mixed) { if(is_scalar($mixed)) { $_SESSION[$mixed] =& $GLOBALS[$mixed]; } elseif(is_array($mixed)) { foreach($mixed as $name) { $ok = session_register($name); if(!$ok) { return false; } } } else { return false; } } return true; } function session_is_registered($name) { if(is_scalar($name)) { return isset($_SESSION[$name]); } return false; } function session_unregister($name) { if(isset($_SESSION[$name]) && is_scalar($name)) { unset($_SESSION[$name]); return true; } return false; } ?>
ZE1 compatibility mode (emulacja modelu obiektowego PHP4) został stworzony aby ułatwić migrację aplikacji z PHP4 do PHP5, ale nigdy nie działał w 100% poprawnie. Nie ma możliwości emulacji zachowania z PHP5. Model obiektowy PHP5/PHP6 jest jedynym obowiązującym.
Biblioteka GD1 nie jest już rozwijana.
Nie ma możliwości emulacji zachowania z PHP5.
Nie można dłużej używać <% zamiast <?php i %> zamiast ?>
Nie ma możliwości emulacji zachowania z PHP5.
Szkoda, ze w natloku tych "nowosci" zaginely gdzies funkcje lambda.
Phar dostępny jest już dla PHP 5.2.x, lecz trzeba go sobie samodzielnie zbudować z PECL. Zostanie włączony oficjalnie do PHP od wersji 5.3.0 i myślę, że jest to jedna z ważniejszych nowości, jakie się pojawią, która uprości dystrybucję skryptów oraz bibliotek.
Phar umożliwia zarówno "odpalenie" całej paczki:
require('./archiwum.phar');
odpala się wtedy jedynie wspomniany w tekście stub, który może np. inicjować autoloader, który na żądanie będzie ładować resztę kodu.
Możliwy jest też dostęp do pojedynczych plików, które, co ważniejsze, nie muszą być wcale skryptami PHP:
include('phar://archiwum.phar/plik.php');
$f = fopen('phar://archiwum.phar/plik.txt');
Phar w zasadzie nie wymaga przeróbek pakowanego skryptu - ludziom udało się już zapakować bez większych trudności kilka popularnych bibliotek, a nawet kompletne skrypty, jak np. phpMyAdmin.
Witam.
Po przeczytaniu tego artykułu uważam, że nowa wersja nie powinna nazywac się 6 tylko powinna byc kolejne wersja 5. Ponieważ nie ma jakiś radykalnych zmian, tylko udogodnienia.
Artykuł bardzo dobry.
Pozdrawiam, Łukasz.