PHP 5.3 незамеченный релиз

Категория: / DEV Блог / PHP (LAMP)
Что нового в PHP 5.3?

PHP 6 не за горами, но разработчики не могут терпеть несправедливости php 5.2, бэкпортировали возможности
шестерки в пятую ветку. Релиз которой состаялся в конце июня!

Насколько я знаю, портеры FreeBSD не сделали порт, поэтому обновляться либо вручную, либо ждать официально поддерживаемого порта.

PHP становится более объектно ориентированным языком - добавлена поддержка неймспейсов, но
корни языка попрежнему дают о себе знать - это и путаница в параметрах haystack-needle и "программистские пробелы"
в названиях функций.

Namespaces - неймспейсы - пространства имён



В бетах php 5.3 разделителем имен был символ "::", потом разработчики хорошо подумали и ими был выбран
новый разделительный символ "\" бэкслеш, аля windows-path-separator. Теперь наш код будет смотреться удивительно
красиво - то ли мы экранируем символы, толи выделяем неймспейсы. мрак..
Вот пример кода. Одному мне кажется, что слеши смотрятся убого и нелепо
(да, теперь все основные фукции явно указываются с глобальным контекстом).

if (empty($this->_options['register-only'])) {
                if (!\file_exists($dest_dir) || !is_dir($dest_dir)) {
                    if (!\mkdir($dest_dir, 0755, true)) {
                        throw new Installer\Exception("failed to mkdir $dest_dir");
                    }
                    Log::log(3, "+ mkdir $dest_dir");
                }


Почему они выбрали этот разделитель?
\1\Тема\на\stackoverflow
\2\RFC-Стори\на\PHP\wiki

PHP всегда был особенным, не совсем логичным языком.

<?php
class User {
 public function set( $attribute, $value ) { ... }
 public function save() { ... }
}
$user = new User();
$user->set('fullname', 'Ben Balbo');
$user->save();


В этом примере объявлен обычный класс User. Однако, если вы используете в своей разработке сторонние
библиотеки (ака фреймворки), то можете встретися с неприятной вещью - конфликтом имен. Именно поэтому
в эпоху динозавров к именам классов-переменныъ приходилось добавлять префиксы. Возьмите тот же zend framework -
все объявления классов начинаются с префикса Zend_, что не очень удобно и читабельно.

Итак, на помощь нам приходят неймспейсы. Кто вообще не в теме, неймспейсы - это так называемые "области видимости" или
можно сказать "контексты выполнения". Ранее все скрипты выполнялись в глобальном неймспейсе-контексте, теперь вы можете
объявить свои, чтобы исключить конфликты.

Смотрите код

<?php
namespace MyCompany\Blog;
               
class User {
       
       public function set( $attribute, $value ) {
               $this->$attribute = $value;
       }
       
       public function save() {
               echo '<p>Blog user ' . $this->fullname . ' saved</p>';
       }
 
}
 
<?php
$user = new MyCompany\Blog\User();
$user->set('fullname', 'Ben Balbo');
$user->save();


Здесь вы явно указываете, что создаете экземпляр класса User именно из необходимого неймспейса.
Вероятнее всего, раньше бы этот класс назывался не иначе как MyCompany_Blog_User.
Сейчас, имеем имя MyCompany\Blog\User.

<?php
namespace MyCompany\CMS;
 
class User {
 
       public function set( $attribute, $value ) {
               $this->$attribute = $value;
       }
       
       public function save() {
               echo '<p>CMS user ' . $this->fullname . ' saved</p>';
       }
 
}


Усложним задачу - добавим еще один класс User, но в другом неймспейсе. Теперь мы можем манипулировать
разными "Юзерами", обращаясь к классу полным именем MyCompany\Blog\User и MyCompany\CMS\User.
The use Keyword

Для того чтобы не перегружать код программы, используются ключевое слово "use",
с помощью которого мы указываем интерпретатору, какой неймспейс мы хотим сейчас использовать.

<?php
use MyCompany\Blog;
$user = new Blog\User();
$post = new Blog\Post();
$post->setUser( $user );
$post->setTitle( $title );
$post->setBody( $body );
$post->save();


C помощью use можно также просто импортировать класс из другого неймспейса.

<?php
use MyCompany\Blog\User;
$user = new User();
Namespace Aliases


Если по каким-то причинам вы не можете использовать одновременно несколько разных классов (из-за конфликтов к примеру),
язык позволяет создать алиасы (синонимы) для обращения к ним.

<?php
use MyCompany\Blog\User as BlogUser;
use MyCompany\CMS\User as CMSUser;
 
$bloguser = new BlogUser();
$bloguser->set('fullname', 'John Doe');
$bloguser->save();
 
$cmsuser = new CMSUser();
$cmsuser->set('fullname', 'John Doe');
$cmsuser->save();


Констаны классов



Константы объявляются следующим образом.
Этот код создаст две константы в неймспейсе test.

namespace test;
define('test\HELLO', 'Hello world!');
define(__NAMESPACE__ . '\GOODBYE', 'Goodbye cruel world!');


Единственное, на константу нельзя сделать алиас.

<?php
namespace MyCompany;
 
class Blog {
       const VERSION = '1.0.0';
}
 
<?php
echo '<p>Blog bersion ' . MyCompany\Blog\VERSION . '</p>';
 
use MyCompany\Blog;
echo '<p>Blog version ' . Blog\VERSION . '</p>';
       
use MyCompany\Blog\VERSION as Foo;
echo '<p>Blog version ' . Foo . '</p>';  
This will result in the following output:
Blog version 1.0.0
Blog version 1.0.0
Blog version Foo


В php 5.3 добавлены новые константы:
__NAMESPACE__ - текущий неймспейс
__DIR__ - текущая директория (ранее определялась dirname(__FILE__))

Функции в неймспейсах



В объектно ориентированном мире нет места функциям - их заменили статичные методы.
В PHP мы можем создавать функции в неймспейсах и обращаться к ним соответственно через их
область имен.

Пример:

<?php
namespace bundle;
function foo() { echo '<p>This is the bundled foo</p>'; }
foo(); // This prints 'This is the bundled foo'
 
<?php
function foo() { echo '<p>This is the global foo</p>'; }
require( 'lib/bundle.class.php');
bundle\foo(); // This prints 'This is the bundled foo'
foo(); // This prints 'This is the global foo'


Явный вызов функции из глобального контекста



Если ваш класс переопределяет метод из глобального неймспейса, вызвать
его можно следующим образом

<?php
namespace bundle;
function foo() { echo '<p>This is the bundled foo</p>'; }
foo(); // This prints 'This is the bundled foo'
\foo(); // This prints 'This is the global foo'



Автолоад классов в неймспейсах



Теперь в __autoload будет передаваться полное имя класса.

__autoload( 'MyCompany\Blog\User' );

Загрузку скрипта можно осуществить следующим образом

function __autoload( $classname ) {
       $classname = strtolower( $classname );
       $classname = str_replace( '\', DIRECTORY_SEPARATOR, $classname );
       require_once( dirname( __FILE__ ) . '
/' . $classname . '.class.php' );
}


Результатом вызова __autoload будет подключение скрипта ./classes/mycompany/blog/user.class.php.

Подробнее о неймспейсах

Late Static Binding, LSB, позднее связывание



Кто-то говорит про паттерн ActiveRecord?

LSB дает возможность вызывать в родительском классе переопределенные в результате наследования методы.

<?php
 
class ParentClass {
   
       static public function say( $str ) {
               self::do_print( $str );
       }
 
       static public function do_print( $str ) {
               echo "<p>Parent says $str</p>";
       }
 
}
 
class ChildClass extends ParentClass {
       
       static public function do_print( $str ) {
               echo "<p>Child says $str</p>";
       }
 
}
 
ChildClass::say( 'Hello' );


Вероятнее всего вы ожидаете результата "Child says Hello", однако теперь результатом выполнения будет "Parent says Hello".
Причина в том что self:: и __CLASS__ резолвятся в имя класса, в котором они используются. PHP 5.3 включает ключевое
слово static::, значение которого указывает на статичный класс, в контексте которого был вызван метод.

static public function say( $str ) {
               static::do_print( $str );
       }


С использованием static\ скрипт выведет "Child says Hello".

__callstatic



До настоящего времени, у PHP в наличии были востребованные "волшебные методы" __set, __get и __call.
PHP 5.3 вводит в оборот новый метод __callstatic , который работает точно также как и __call,
за исключением того что он оперирует со статичным контекстом.

<?php
 
class Factory {
 
       static function GetDatabaseHandle() {
               echo '<p>Returns a database handle</p>';
       }
 
       static function __callstatic( $methodname, $args ) {
               echo '<p>Unknown static method <strong>' . $methodname . '</strong>' .
                       ' called with parameters:</p>';
               echo '<pre>' . print_r( $args, true ) . '</pre>';
       }
}
 
Factory::GetDatabaseHandle();
Factory::CreateUser();
Factory::CreateBlogPost( 'Author', 'Post Title', 'Post Body' );


Теперь вы можете вызывать динамически статичные методы.

$classname = 'Factory';
$methodname = 'CreateUser';
$classname::$methodname();
 
$methodname = 'CreateBlogPost';
$author = 'Author';
$posttitle = 'Post Title';
$postbody = 'Post Body';
 
$classname::$methodname( $author, $posttitle, $postbody );


Вы можете динамически создавать объекты.

<?php
require_once( 'lib/autoload.php' );
 
$class = 'MyCompany\Blog\User';
$user = new $class();
$user->set('fullname', 'Ben Balbo');
$user->save();


Хотя это не приветствуется, потому как затрудняет отладку, читаемость кода.

MySQL Native Driver - mysqlnd



Нативный драйвер mysql. Улучшена производительность благодаря интеграции библиотеки mysql и движка zend.
Также значительно улучшена работа драйвера с памятью, так как она теперь выделяется не напрямую, а с помощью
механизмов zend.

Подробнее о mysqlnd

E_STRICT



Да, да - теперь E_ALL по-умолчанию включает этот режим.
Также добавлена константа E_DEPRICATED, ошибки с уровнем которой будут возникать в случае использования функций,
помеченных как "устаревшие". И такие наверняка будут, потому как например "устарели"

call_user_method();
ereg(); // (use preg_match() instead)
ereg_replace(); // (use preg_replace() instead)


Полный список устаревших конструкций

Статья - вольный перевод http://www.sitepoint.com/article/whats-new-php-5-3/

Замыкания - closures



Раньше php не позволял создавать простые анонимные функции (функции без имени), вместо этого в языке была предусмотрена функция create_function.
Основное назначение анонимусов - в фукциях обратного вызова (callbacks).

echo preg_replace_callback('~-([a-z])~', function ($match) {
    return strtoupper($match[1]);
}, 'hello-world');
// outputs helloWorld


В php 5.3 все объявления анонимных функций автоматически создаются как экземпляры внутреннего класса Closure.

$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};
$greet('World');


Замыкания могут создаваться внутри других методов. Для использования переменных из родительского контекста, их нужно явно
указать в заголовке функции.

public function getTotal($tax)
    {
        $total = 0.00;
       
        $callback =
            function ($quantity, $product) use ($tax, &$total)
            {
                $pricePerItem = constant(__CLASS__ . "\PRICE_" .
                    strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };
       
        array_walk($this->products, $callback);
        return round($total, 2);
    }


Страница мануала по замыканиям

Тернарный оператор (ternary)



Обычная версия его выполняет роль if-then-else и используется следующим образом

(expr1) ? (expr2) : (expr3)  // if (expr1) =expr2 else =expr3


С появлением PHP 5.3, возможно отбросить среднюю часть оператора (та что true) и сразу перейти к ложному условию.
Первая часть в таком случае равна самому условию.

expr1 ?: expr3 // if (expr1) =expr1 else =expr3


Страница мануала по тернарному оператору

Дополнительная информация по миграции с PHP 5.2 на 5.3

http://ru2.php.net/migration53