Wyszukiwarka
Logowanie
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.