Traits в php 5.4. Разбираем детали реализации

  • Tutorial
Совсем недавно вышла первая beta php 5.4, а пока я писал топик подоспела и вторая. Одно из нововведений в 5.4 – это traits (типажи). Предлагаю разобраться во всех деталях в том, что же типажи из себя представляют в php.

Простой пример типажа, чтобы не заглядывать в Википедею:
//определение типажа
trait Pprint 
{
    public function whoAmI()
    {
        return get_class($this) . ': ' . (string) $this;
    }
}

class Human 
{
    use Pprint; //подключаем типаж, ключевое слово use

    protected $_name = 'unknown';
    
    public function __construct($name)
    {
        $this->_name = $name;
    }

    public function __toString()
    {
        return (string) $this->_name;
    }   
}

$a = new Human('Nikita');
echo $a->whoAmI(), PHP_EOL; //=> Human: Nikita

Как видно, к классу Human было добавлено поведение из типажа Pprint.

Но во всём есть свои детали.

Синтаксис


В общем и целом всё просто. Типажей можно подключить к классу неограниченное кол-во через одну или несколько конструкций use внутри определения класса. use может быть указан в любом месте класса.

Дополнительно в блоке ({...}) после use можно:
  • назначить alias'ы к методам типажа (Trait::method as myMethodmethod из Trait будет дополнительно доступен, как myMethod);
  • указать перекрытие метода одного типажа, методом другого, если у них совпали названия (TraitA::method insteadof TraitB – будет использован метод TraitA вместо одноимённого метода TraitB);
  • повысить или понизить доступ к методу из типажа, за исключение перевода метода в статический (Trait::publicMethod as protected), можно сразу с переименованием (Trait::publicMethod as protected _myProtectedMethod).

Сам типаж записывается, как trait и может включать другие типажи, через указание их в ключевом слове use. Cинтаксис и возможности аналогичны use в классе.

Более сложный пример:
trait Pprint 
{
    public function whoAmI()
    {
        return get_class($this) . ': ' . (string) $this;
    }
}

trait Namer 
{
    //использование одного типажа в другом
    use Pprint;
    
    public function getMyName()
    {
        return $this->whoAmI();
    }
    
    public function getMyLastName()
    {
        return 'Unknown =(';
    }
    
    public function getMyNickname()
    {
        return preg_replace('/[^a-z]+/i', '_', strtolower($this->getMyName()));
    }
}

trait SuperNamer
{
    public function getMyLastName()
    {
        return 'Ask me';
    }
}


class Human 
{
    use SuperNamer;
    use Namer
    {
    	SuperNamer::getMyLastName insteadof Namer;
    	Namer::getMyNickname as protected _getMyLogin;
    }

    protected $_name = 'unknown';
    
    public function __construct($name)
    {
        $this->_name = $name;
    }

    public function __toString()
    {
        return (string) $this->_name;
    }
    
    public function getLogin()
    {
        return $this->_getMyLogin();
    }
    
    
}

$a = new Human('Nikita');

echo join(', ', get_class_methods($a)), PHP_EOL;
//__construct, __toString, getLogin, getMyLastName, 
//getMyName, getMyNickname, whoAmI

echo $a->getMyName(), PHP_EOL; //Human: Nikita
echo $a->getMyLastName(), PHP_EOL; //Ask me
echo $a->getLogin(), PHP_EOL; //human_nikita
echo $a->getMyNickname(), PHP_EOL; //human_nikita

Тут важно обратить внимание на два момента. Во-первых, блок после use кажется связанным с типажом около которого он описан, но это не так. Правила в блоке глобальные и могут быть объявлены в любом месте.

Чтобы не возникало путаницы, хорошей практикой будет записать сначала все типажи через запятую, а затем на отдельной строке правила перекрытия и alias. Либо описывать все правила для типажа рядом с его подключением. Выбор за вами.
//так
use SuperNamer, Namer, Singleton, SomeOther
{
    SuperNamer::getMyLastName insteadof Namer;
    SomeOther::getSomething as private;
}

//либо так
use Namer;
use Singleton;
use SuperNamer
{
    SuperNamer::getMyLastName insteadof Namer;
}
use SomeOther
{
    SomeOther::getSomething as private;
}

Во-вторых, обратите внимание на список методов, в списке остался getMyNickname, а _getMyLogin просто его alias с пониженным доступом. Можно исключить исходный метод совсем, но об этом ниже в разделе магии.

Типажи инициализируются, как и классы, динамически. При большом желании можно писать так:
if ($isWin) {
    trait A { /* … */}
} else {
    trait A { /* … */}
}

Свойства в типажах


До этого, я оперировал методами, но типаж может включать в себя и свойства, которые будут добавлены в класс. В этом плане «типажи» в php – это скорее mixin.
trait WithId 
{
    protected $_id = null;
    
    public function getId()
    {
        return $this->_id;
    }
    
    public function setId($id)
    {
        $this->_id = $id;
    }
}

Сразу предлагаю хорошую практику, чтобы однажды не оказалось, что свойство _id в типаже конфликтует с используемым в классе или его потомках, свойства типажей записывать с префиксами:
trait WithId 
{
    protected $_WithId_id = null;
    protected $_WithId_checked = false;
    //...
    
    public function getId()
    {
        return $this->_WithId_id;
    }
    
    public function setId($id)
    {
        $this->_WithId_id = $id;
    }
}

Область видимости


Важно понимать, как будут разрешаться различные вызовы внутри типажа. В этом поможет правило думать о подключении типажа, как о «copy-paste» кода в целевой класс. В самом первом примере, интерпретатор как бы сделал «copy-paste» метода whoAmI в класс Human, соответственно все вызовы к parent, self, $this будут работать также, как и вызов в методах класса. Исключение будут составлять некоторые магические константы, например внутри whoAmI __METHOD__ === 'Pprint::whoAmI'.

Внутри методов типажа доступны все свойства объекта для обращения напрямую, никаких дополнительных областей видимости не добавляется. Можно было бы получить просто $this->_name, вместо вызова __toString. Однако стоит несколько раз подумать, прежде чем делать это, так как на сложных реализациях это внесёт не мало путаницы. Я бы рекомендовал всегда использовать понятные методы, при необходимости даже описать их в интерфейсе и «заставлять» классы его имплементировать.

Статические методы и свойства


В типаже можно объявлять статические методы, но нельзя объявлять статические свойства. Внутри статических методов можно использовать, как статическое связывание (self::), так и динамическое (static::), всё будет работать так, как будто вызвано из метода класса («copy-paste»).

Ограничение на хранение статических свойств обойти можно, как именно покажу позже с обращением к магии.

Совпадение методов типажей между собой и с методами класса


Метод описанный в классе перекрывает метод из типажа. Но если какой-то метод описан в родительском классе, а в дочернем классе подключён типаж с таким же методом, он перекроет метод из родительского (снова вспоминаем «copy-paste»).

Если в нескольких, указанных у класса типажах, используются одинаковые методы, php выдаст ошибку на этапе инициализации класса:
trait A
{
    public function abc() {}
}

trait B
{
    public function abc() {}
}

class C 
{
    use A, B;
}
//Fatal error: Trait method abc has not been applied, 
//because there are collisions with other trait methods
//on C in %FILE% on line %line%
На помощь приходит insteadof, с помощью которого нужно будет разрешить все коллизии.

Хитрая ошибка может быть в случае, когда в классе тоже определён метод, вызвавший коллизию, в таком случае php пропустит эту проверку, т.к. он проверяет только «выжившие» методы типажа:
trait A
{
    public function abc() {}
}

trait B
{
    public function abc() {}
}

class C 
{
    use A, B;
    
    public function abc() {}
    
}
//OK
Когда-нибудь потом, перенеся метод abc в родительский класс, получим странную ошибку по коллизии методов типажей, которая может сбить с толку. Так что, коллизии лучше разрешить заранее. (С другой стороны, если в коде методы типажа и класса совпадают, возможно что-то уже не так.)

Совпадение свойств типажа со свойствами другого типажа и свойствами класса


В этом моменте нас поджидают неприятные проблемы. Сразу пример:
trait WithId 
{
    protected $_id = false;
    //protected $_var = 'a';
    
    public function getId()
    {
    	return $this->_id;
    }
    
	//...
}

trait WithId2 
{
    protected $_id = null;
    
    //protected $_var = null;
    //...
}

class A 
{
    use WithId, WithId2;
}

class B 
{
    use WithId2, WithId;
}

class C
{
	
	use WithId;
	
	protected $_id = '0';
}
//

$a = new A();
var_dump($a->getId()); //NULL

$b = new B();
var_dump($b->getId()); //false

$c = new C();
var_dump($c->getId()); //false (!)

//Если раскомментировать $_var
// WithId and WithId2 define the same property ($_var)
// in the composition of A. However, the definition differs 
// and is considered incompatible. Class was composed 
// in %FILE% on line %LINE%

Поясняю. В общем случае при пересечении свойств типажей между собой или свойств типажа и класса выдаётся ошибка. Но зачем-то для «совместимых» свойств делается исключение и они работают по принципу «кто последний, тот и прав». Поэтому в классе A в getId получилось NULL, а в классе B – false. При этом свойства класса считаются ниже, чем свойство типажа (с методами равно наоборот) и в C вместо ожидаемого '0' получим false.

Совместимыми считаются значения нестрогое сравнение которых даёт true, а так как в php при этом много неявных преобразований, могут быть неприятные ошибки при использовании строго сравнения возвращаемых значений.
var_dump(null == false); //true
var_dump('0' == false); //true
var_dump('a' == null); //false

Так что практика с префиксами, предложенная выше, будет полезна и в таких случаях. Я же надеюсь что эту часть реализации ещё пересмотрят к релизу.

Ошибки и исключения в типажах


Если следовать мнемоническому правилу trait == «copy-paste», с ошибками становится сразу всё понятно:
<?php trait Slug 
{
    public function error()
    {
        echo $this->a; //5
    }
    
    public function someMethod()
    {
    	$this->error();
    }
    
    public function testExc()
    {
    	throw new Exception('Test'); //16
    }
}

class Brain 
{
    use Slug;
    
    public function plurk()
    {
    	$this->testExc(); //25
    }
}

error_reporting(E_ALL);
$b = new Brain();
$b->someMethod();
//Notice: Undefined property: Brain::$a in %FILE% on line 5

try {
       $b->plurk(); //35
} catch(Exception $e) {
	echo $e;
}
// exception 'Exception' with message 'Test' in %FILE%:16
// Stack trace:
// #0 %FILE%(25): Brain->testExc()
// #1 %FILE%(35): Brain->plurk()
// #2 {main}

Объект уже не знает, откуда у него взялся метод в котором был Notice или Exception, но это можно узнать в stack trace по строкам кода, в которых были вызовы. Если хранить типажи в отдельных файлах определить будет ещё проще.

Немного белой чёрной магии


Покажу пару грязных приёмов с типажами, используйте их на свой страх и риск.

Удаление метода типажа


Чтобы удалить метод типажа, например, когда ему был задан alias, можно сделать так:
trait A
{
    public function a() {}
    
    public function b() {}
}

trait B
{
    public function d() 
    {
    	$this->e();
    }
    
    public function e() {}
}

class C 
{    
    use A
    {
    	//удаляем и переименовываем
    	A::b insteadof A;
    	A::b as c;
    }
    
    use B
    {
    	//удаляем метод совсем
    	B::e insteadof B;
    }
}

echo join(", ", get_class_methods('C')), PHP_EOL;
//a, c, d

Но в таком подходе таится большая опасность, т.к. одни методы типажа потенциально могут вызывать другие методы:
$c = new C();
$c->d();
//Fatal error: Call to undefined method C::e()

При переименовании типаж ничего не знает о том, что метод был переименован. Поэтому по-умолчанию при указании alias'а сохраняется оригинальный метод.

«Наследование» в типажах


С помощью похожего трюка можно реализовать «наследование» в типажах c возможностью вызова «родительских» методов.
trait Namer 
{
    public function getName()
    {
        return 'Name';
    }
}

trait Namer2
{
    public function getName()
    {
        return 'Name2';
    }
}

trait Supernamer 
{
    use Namer, Namer2
    {
        Namer::getName insteadof Namer;
        Namer::getName as protected _Namer_getName_;
        
        Namer2::getName insteadof Namer2;
        Namer2::getName as protected _Namer2_getName_;
    }
    
    public function getName()
    {
        return  $this->_Namer_getName_() . $this->_Namer2_getName_();
    }
    
}

Два способа реализовать Singleton с помощью типажей


Чтобы сгладить это магическое безобразие покажу один полезный пример. Часто в виде типажа приводят Singleton, хотя без возможности задания в типаже статической переменной сделать его будет не так просто, как кажется на первый взгляд. Можно воспользоваться двумя хитростями.

Первая – получить внутри вызываемого метода название класса, к которому он был вызван, а затем в качестве хранилища воспользоваться отдельным классом со статическим методом, примерно так:
trait Singleton {
    
    static public function getInstance()
    {
        $class = get_called_class(); //работает аналогично static::
        
        if (!Storage::hasInstance($class)) {
            $new = new static();
            Storage::setInstance($class, $new);
            
        }
        return Storage::getInstance($class);
    }
}

Вторая – воспользоваться толи фичей, толи багой php, которая связана с использованием ключевого слова static при объявлении переменной. Эти переменные должны сохранять своё значение при вызовах метода, но видимо структура для хранения этих переменных инициализируется в каждом месте использования метода. В итоге получается такая схема:
trait Singleton 
{
    static public function getInstance()
    {
        static $instance = null;
        if ($instance === null) {
            $instance = new static();
        }
        return $instance;
    }
}

class MyClass 
{
    use Singleton;
}

class MyExtClass extends MyClass {}

echo get_class(MyClass::getInstance()), PHP_EOL; //MyClass
echo get_class(MyExtClass::getInstance()), PHP_EOL; //MyExtClass


P.S.

Спасибо aveic за помощь и интересные идеи по работе с типажами.
Метки:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 102
  • +1
    Спасибо за статью. Достаточно интересно.
    • +11
      >> Чтобы не возникало путаницы
      Вот, золотые слова.

      При всём при том, что PHP мой основной язык, я жду двух вещей от типажей:
      * Новый тип говнокода с типажами, в котором будет сложно разобраться.
      * Малая распространённость типажей, ибо не понятно зачем они нужны (для множественного наследования говорите? Как часто это вам надо?).

      • +1
        Ну понятно что для движка хоумпэйджа Васи Пупкина типажи не нужны. А вот для всяческих CRM,ERP (и т.п.) систем — очень даже надо. Конечно сейчас из этого добра мало что создается на PHP (процентов 20 от силы) — но так ведь развивается язык, типажи еще один шажок вперед.
        • +7
          Я думаю что многие бы предпочли увидеть другой «шажок вперёд» вместо типажей
          • 0
            Какой, например?
            • +2
              Unicode, вестимо.
              • 0
                что конкретно вам даст тот факт, что строки внутри будут храниться в UCS2?
                увеличится скорость работы с ними? всё магически станет работать лучше?
                это еще один чисто внутренний момент, который вас интересовать не должен.
                • 0
                  Хотя бы то, что не нужно будет задумываться о том как называется та или иная функция, кроме того не для всех функций есть соответствующий аналог. Реальный пример — как вы сейчас получаете символ с определенной позицией из строки в UTF-8?
                  • +1
                    >Хотя бы то, что не нужно будет задумываться о том как называется та или иная функция
                    Т.е. при переходе на внутренний Unicode в мануал смотреть не надо будет?

                    >Реальный пример — как вы сейчас получаете символ с определенной позицией из строки в UTF-8?

                    $text = «тест»;
                    $second_utf8_symbol = substr($text, 1, 1);

                    Естественно, я предварительно добавляю это в конфиг:
                    mbstring.internal_encoding=utf8
                    mbstring.func_overload=2

                    А теперь давайте подумаем что реально получается при полном переходе на юникод внутри.
                    1) все операции со строками должны проходить через ICU (http://icu-project.org), которая отнюдь не самая быстрай библиотека в мире, в том числе и по объективным причинам — операции со строками замедляются и довольно чувствительно.
                    2) все хэш-лукапы, поиски методов в списках методов классов, поиски самих классов и т.п. тоже включают в себя операции сравнения строк, поэтому и они быстрее не станут.
                    3) поскольку юникод по определению больше размером, то и объемы потребляемой памяти растут.
                    4) массу уже существующего кода придется переписать из-за того, что люди изначально закладываются на тот факт, что строка это просто данные, а не корректная строка в определённой кодировке.
                    5) имеем массу веселья с кодировками:
                    — данные от юзера — в какой кодировке?
                    — текст скрипта — в какой кодировке?
                    — файловая система — в какой кодировке?
                    — вывод скрипта — в какой кодировке?
                    — данные в базе — в какой кодировке?
                    и т.д. и т.п.
                    Всё это надо привести к внутренней кодировке ICU, для всего этого нужны какие-то INI-опции.

                    При этом mbstring работает нормально, а есть еще и php.net/intl для более низкоуровневой работы с Юникодом.

                    Вот и приходим мы к тому, что толком это никому не нужно.
                    Никто просто не заинтересован в инвестировании нескольких человеко-лет ради совершенно призрачного результата при таких очевидных минусах.

                    Нет, я согласен, ПРАВИЛЬНО было бы именно так. Но действительность говорит сама за себя — на нативную поддержку Unicode сейчас нет спроса. Был бы спрос — уже бы всё сделали.

                    И да, если вам надо и вы готовы — я могу вам помочь со стартом, нет проблем.
                    • 0
                      > $second_utf8_symbol
                      А если нужен 10 символов? Или все символы в строке? Регекспы нам помогут?

                      Я бы предпочел нативную поддержку [].

                      > А теперь давайте подумаем
                      1) Почему именно должны? неужели нет ни одной альтернативы?

                      2) Не верю что это нельзя оптимизировать. Кэширование байткода разве не решает проблему?

                      3) В данном временном промежутке это не проблема (память копейки стоит), в тоже время вы упускаете тот факт, что всякие костыли типа регекпов для получения массивов символов также требую памяти и времени.

                      4) Ничего не придется переписывать, проблемы могут быть только в том, случае, если вы решитесь обновить древний скрипт работающий на древней версии PHP да еще и сохранённый в CP1251. Вот только зачем ему современные плюшки?

                      В нормальных скриптах разницы не будет, ибо mb_* останется и продолжить работать как раньше, а str* или все также будут перекрываться mbstring.func_overload или начнут нормально работать и без этой настройки.

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

                      5) Браузер посылает данные в той же кодировке что и станица (у форм, кстати, есть атрибут accept-charset), про скрипты см. выше, вывод — отдельная настройка, БД все равно нужно указывать. Не вижу здесь каких либо серьезных проблем.

                      > Вот и приходим мы к тому, что толком это никому не нужно.

                      OpenSource, реализуется в первую очередь то, что хотят сами разработчики языка или за что им платят, поэтому данное высказывание не корректно, и, ИМХО, многим пишущим на PHP это нужно, другое дело, что за прошедшее время каждый из них уже реализовал необходимые функции (=костыли).

                      Вдогонку, наткнулся на старую презентацию: «Unicode & PHP6» (http://www.slideshare.net/kfish/unicode-php6-presentation), там есть решения некоторых проблем. Да и ранее достаточно активно велась работа на юникодом, текущую печальную ситуацию кто-то описывал на в одном из комментариев к какой-то статье, найти, к сожалению, так и не смог.
                      • 0
                        >А если нужен 10 символов?

                        Поменяйте параметр «длина» у функции.

                        >Или все символы в строке? Регекспы нам помогут?

                        Все символы в строке? $text — даю гарантию, что все символы в строке — в строке.

                        >1) Почему именно должны? неужели нет ни одной альтернативы?

                        Потому, что там наиболее полная поддержка юникода и эта либа — стандарт де-факто.
                        Вы же, наверное, и на частичной поддержке не остановитесь, да?

                        >2) Не верю что это нельзя оптимизировать. Кэширование байткода разве не решает проблему?

                        Чего? Хэш-лукапов и работы со строками?
                        Кэширование байткода нужно только для того, чтобы не делать компиляцию кода в байткод на каждый запрос.
                        Но код-то еще и выполнять надо.

                        >3) В данном временном промежутке это не проблема (память копейки стоит),
                        >в тоже время вы упускаете тот факт, что всякие костыли типа регекпов для
                        > получения массивов символов также требую памяти и времени.

                        Да, костыли требуют времени тех, кому они необходимы.
                        А вы предлагаете всем пользователям принудительно докупить памяти.

                        >4) Ничего не придется переписывать, проблемы могут быть только в том, случае,
                        >если вы решитесь обновить древний скрипт работающий на древней версии PHP

                        >В нормальных скриптах разницы не будет, ибо mb_* останется и продолжить
                        >работать как раньше, а str* или все также будут перекрываться
                        >mbstring.func_overload или начнут нормально работать и без этой настройки.

                        А вы попробуйте, исходники же есть: svn.php.net/viewvc/php/php-src/branches/FIRST_UNICODE_IMPLEMENTATION/

                        >OpenSource, реализуется в первую очередь то, что хотят сами разработчики языка
                        >или за что им платят, поэтому данное высказывание не корректно, и, ИМХО,
                        >многим пишущим на PHP это нужно, другое дело, что за прошедшее время каждый
                        >из них уже реализовал необходимые функции (=костыли).

                        И судя по всему, они их устраивают, т.к. я именно про это и говорю — заинтересованных разработчиков нет (да и был один Змиевский по большому счету), а нанимать кого-то и платить за разработку никто не хочет. Вот это я и подразумеваю под «нет спроса».
                        • 0
                          > Поменяйте параметр «длина» у функции.
                          Вы не поняли, я про тот случай когда нужно преобразовать строку в массив символов, сейчас единственный способ это сделать — использование регеспов. Разве не костыль?

                          > А вы предлагаете всем пользователям принудительно докупить памяти.
                          Это все слова, как я понимаю, статистики по увеличению потребления памяти у вас нет? (у меня нет)

                          > А вы попробуйте
                          Нет, спасибо.

                          Если сами пробовали может быть поделитесь результатами?
                          • 0
                            Я сужу на основе своего опыта работы с PHP6 — я довольно много phpt-тестов под него правил и чуток участвовал в разработке.
                            Понятное дело, нигде в продакшене я его не поднимал.
              • 0
                unicode как описали ниже.
                Упорядочивание каши с именами функций. и тэ дэ и тэ пэ
                • 0
                  Давайте начнём упорядочивать кашу с C, на котором написан PHP.
                  O_CREAT — бардак! Где «E» на конце?
                  И надо не забыть сделать версию для camelCase-людей — oCreate, для ОО-людей — OpenAttributes::getInstance(O_CREATE), надо же учитывать эстетические вкусы всех.
                • 0
                  mixin
            • +16
              Traits и mixins — дико полезные штуки, когда мы хотим сделать какое-то свойство (особенность), которую можно давать объектам. Напимер, в RoR через миксины сделаны расширения к моделям: можно к любой модели добавить какую-то опцию.

              Но в PHP, как всегда, все сделано криво: нет функции, вызываемой при приаттачивании trait, нет возможности объекту опросить список своих trait. Соответственно, как наладить взаимодействие между базовым объектом и примесью, и как им узнать друг о друге, непонятно.
              • +5
                Что то похожее я видел в Yii, но там это называется Behaviors, и логика работы схожая.
                • +1
                  Ага, очень удобная штука. Недостает только поддержки в IDE
                  • 0
                    ну IDE можно указать, через спец комментарии, как к примеру в NetBeans это делается
                    /* @var $varname ClassName */
                  • 0
                    Да, в Yii это фактически mixin.
                  • +1
                    Ключевое тут trait != mixin. Я не говорю, что примеси — плохо, я только говорю, что в PHP реализованы именно трейты — они не имеют состояния. (во всяком случае не должны иметь), поэтому подключаются во время компиляции, а не динамически.
                  • +1
                    Понятие DRY, как я вижу, вам неведомо.
                  • 0
                    Предвижу как минимум два типажа: CommentableEntity и TaggableEntity
                    • 0
                      Не выйдет т.к. trait фактически является копи-пейстом, а тут нужно состояние для того, чтобы данные entity сконфигурировать.
                    • +12
                      * Весь говнокод в голове. Никакая парадигма не спасет от этого.
                      * Нужны почти всегда если написать в MVC и DRY. При наличии типажей и примесей можно делать очень тонкие контроллеры. Вот например вам не нужно в каждом контролере аутентификация или сессии или мэйлер. Так почему бы не подключить когда они нужна user Session, Auth, Mailer;(конечно можно через глобальный реестр делать вызов их, но тут как кому нравиться) Или реализовать фильтры как цепочка вызовов use CSRFFilter, DoSFilter; Т.е. теперь не нужно лепить кучу статических методов чтоб может когда-то инициализировать какое то свойство. Вот в чем вся и прелесть.
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • 0
                          Видимо вам не знакомо что такое псевдокод или… вы вообще не защищаете ресурсоемкие операции надеясь только на сервер :-)
                          • +3
                            DoS и DDoS это разные вещи. DoS-атакой может являться запуск любой ресурсоемкой операции злоумышленником (запрос картинки в 100500х100500 пикселей там, где она генерируется автоматически скриптом, специально сформированный запрос, который вызовет чтение большого кол-ва строк из БД и т.п.). От атак этого типа можно защититься с помощью проверки данных, полученных от пользователя, на попадание в допустимый диапазон. Естественно, такая проверка должна проводиться кодом, который обладает знаниями об это диапазоне.
                          • 0
                            Я с вами не соглашусь. Говнокод не в голове, а в коде. В голове наоборот все стройно и красиво.
                            Ваш подход к трейтам — это инклюды. Как раньше можно добавить своего функционала в выполняемом файлике, сделав инклюд с неким кодом, так и теперь, можно загрузить трейт, чтобы добавить функционала к существующему контроллеру.
                            Что будет на самом деле: некоторое время назад я столкнулся с подобным кодом: несколько сотен точек входа, порядка 50 таких магических файликов для инклюдов, в которых происходили танцы вокруг глобальных переменных, которые они либо предоставляли, либо модифицировали. Что бы добавить какой-то функционал в проект, надо было открыть соседнюю точку входа, скопировать из нее «шапку» — набор инклюдов для инициализаци в нужном порядке, и уже потом писать свой код. С вашими трейтами будет то же самое. Пока их не много — они помещаются у вас в голове(все пять). По мере развития проекта вы перестанете понимать что в них происходит. В них появятся костыли с неявной функциональностью, они будут частично дублироваться. А сколько информации нужно будет загрузить в мозг новичку в вашем проекте?
                            По большому счету — к пхп коду предъявляется только одно требование — понятность. Мое мнение — трейты понятность не увеличивают.
                            • 0
                              Причем тут инклуды вообще?! С вашей логикой можно сказать что и MVC это инклуды, а что разве нет? Те же инклуды только происходят в определенном порядке. Идя по вашей логике можно сказать что http это вообще набор слов и чисел, что будет верно но далеко от истины. Как вы приплели сюда к трэйтам стандартные голые включения php кода я не знаю. Вы пишите кроме пхп на каком то языке где есть трэйты? Как можно судить даже не попробовав, у меня это в голове не укладывается.
                              >> А сколько информации нужно будет загрузить в мозг новичку в вашем проекте
                              Так давайте вообще ООП c php выкинем, а то у бедного новичка мозг кипятится.
                              >> одно требование — понятность
                              Я может вам глаза приоткрою но пхп чуть ли не худший язык чтобы понимать что-то глядя на исходный код. Так как в большистве случаев без проверки var_dump вы даже не знаете какой тип имеет переменная или возвращает метод. А поскольку многие пхпшники не знают что такое тестирование баг и php код как брат с сестрой.
                              >> Пока их не много — они помещаются у вас в голове(все пять)
                              В голове абсолютно ничего держать не надо, если код покрыт тестами. Если вам нравиться держать все в голове это ваше право, если же у вас есть архитектура, есть тесты или например TDD, то вы не сможете по крупному наговнокодить. Поэтому говнокод только в голове.
                          • +3
                            В работе с ORM очень полезный функционал, очень удобно добавлять определенное поведение каким-то группам моделей, при этом не встраиваясь в цепочку наследования.
                            • 0
                              > Как часто это вам надо

                              Постоянно. Попробуйте один разок и уже не сможете без этого жить. В yii это сейчас эмулируется, а теперь можно будет дождаться натиную реализацию.
                              • 0
                                Не заработает. Для того, чтобы заменить behavior нужны mixin, а не trait.
                              • +1
                                Зря. Очень даже много поводов использовать. Говнокода, конечно, станет больше, как от любого другого мощного синтаксиса.
                              • 0
                                Хорошая и полезная фича. Скорее бы стабильный релиз, на продакшн бета не годиться к сожалению.

                                З.Ы. Вот еще бы множественные конструкторы запилили
                                • 0
                                  Динамическая типизация мешает перегрузке конструкторов и методов.
                                  • 0
                                    Пожалуйста раскройте — почему так?
                                    • 0
                                      Например в яве компилятор определяет какую функцию вызвать по сигнатуре (имя функции, количество и тип аргументов). В php перегрузку можно было бы сделать по количеству аргументов, но почему то предпочли сделать аргументы со значениями по умолчанию и я думаю у них были причины на этот счет.
                                      • +5
                                        При чём здесь динамическая типизация? Авторы, скорее, не захотели ограничивать людей, которые исторически работали с func_get_args. Да и с точки зрения прекомпиляции гораздо проще ходить по детерминированному дереву операций, нежели в процессе ещё и подбирать с помощью паттерн-матчинга нужную сигнатуру.

                                        PHP — это не питон, где можно поломать что-то на грани версий и сказать «Ок, живите с этим». Это колоссальное сообщество с миллиардами легаси-скриптов, которые нельзя ломать. Если делать перегрузку так, как это происходит в других языках, то надо в случае ненахождения сигнатуры метода не вызывать метод, а аргументы передавать опциональными, а бросать ыксепшон, перед этим вызвав какой-нибудь magic method. Представляете, что начнётся с легаси-кодом?

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

                                        Сколько можно умным людям (не мне, а всяким малоизвестным Грэмам, например; или Кеям) писать: объектно-ориентированное программирование — это парадигма, идеология, стиль мышления. Это не виртуальные таблицы методов с сахаром для перегрузки в локальном контексте переменных this, self и прочих им подобных. Это не замещающие дженерики. Это представление мира в виде сущностей, имеющих интерфейсы для обмена данными. В крайнем случае это данные, имеющие правила, по которым идёт их обработка в суперпозиции с другими данными. Это не типизация, это не наследование, это не замыкание данных.

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

                                        Я пишу на PHP уже 13 лет. Не то, чтоб это много, но я застал ещё php 3.0 и успел поковыряться в её внутренностях, потом изредка посматривая на внутренности следующих поколений. Это замечательный инструмент, который внутри недалеко ушёл по сложности устройства от консоли Quake 2. Это автомат Калашникова. И не надо обвешивать замечательный инструмент тем, для чего созданы более впечатляющие и более подходящие как по внутреннему устройству так и по идеологии языки. И старое испортите и новое потеряете.

                                        Вы извините за эмоции, надоело смотреть на реплики «АВТОРЫ ГОРИТЕ В АДУ БЕЗ МНОЖЕСТВЕННОГО НАСЛЕДОВАНИЯ У НАС ЕСТЬ ПАТТЕРНЫ КОТОРЫЕ МЫ РЕАЛИЗУЕМ. НАМ НАДО ИХ ПОМНОЖИТЬ И ПОЛУЧИТЬ СУПЕРОБЪЕКТЫ СУПЕРРЕАЛЬНОСТИ ДЛЯ АБСТРАКЦИЙ ОТ АБСТРАКЦИЙ!». У вас есть интерфейсы, у вас есть наследование, у вас есть магические методы. Да, для них надо писать абстракции. Да, в этом случае вы становитесь ответственны за архитектуру. Но, блин, этого же никто не хочет, для этого есть паттерны, есть строгая типизация и оптимизирующие компиляторы, умеющие связывание по сигнатурам.

                                        Плохому программисту типизация мешает, простите.

                                        Минусуйте, гоните, насмехайтесь.
                                        • 0
                                          Все это очень интересно и я во многом с вами согласен, но какое отношение ваш опус имеет к моему ПРЕДПОЛОЖЕНИЮ «почему в php нет перегрузки функций»?
                                          • +3
                                            Вчитайтесь в мой опус ещё раз, пожалуйста. Прочитайте труды Кэя, Грэма, Абельсона, Себесту. Почитайте код PHP. Оцените трудозатраты. Оцените профит. Оцените нужность этого и риски. Все ответы перед вами, нужно только подумать.

                                            Мой опус — исключительно ответ на ваше, скажем так, не очень обоснованное высказывание про то, что, мол, динамическая типизация мешает сделать перекрытие/перегрузку функций. Думать за вас и обосновывать ваши ПРЕДПОЛОЖЕНИЯ я не собираюсь, у вас для этого есть своя голова. Приведите аргументацию, пожалуйста. Мифическая невозможность сигнатурного матчинга в данном случае не аргумент, поскольку а) PHP поддерживает уточнение типизации при вызове методов, б) даже если предыдущий пункт чем-то смущает вас, то есть мнение, что сигнатуры при динамической типизации не требуют спецификации типов.

                                            Видите ли, людям, которые квадратно-гнездовым методом забивают паттерны синглтонами кверху в шаблонно насверленные по кернам мануалов отверстия иерархической пустоты что-либо объяснять очень тяжело. Надеюсь, вы не такой.

                                            Если вам действительно интересно и вы со мной согласны (как вы написали выше), то вы наверняка знаете, что можно очень изящно с помощью интерфейсов и паттернов декорации замечательно развязать практически любую иерархию за исключением той, в которой насильно вводится употребление объектов по общему типажу (или, если будет угодно, дженерик-классу) с параметризированной типизацией. Но это уже специфические либо клинические случаи архитектурной индустрии.

                                            Я бы мог уместить содержание своего опуса в пять слов — «Потому что это не нужно». Я не стал сознательно этого делать, потому что если есть возможность совратить с пути «потенциально гнездового» мышления заблуждающегося человека — ему нужно подставить видимую подножку, дав при этом возможность её увидеть и понять, как больше не натыкаться на такие подножки.

                                            Почитайте литературу по Smalltalk, например. Она замечательно объясняет, почему ООП Страуструпа — это не ООП, а PHP — это не то, чему нужна перегрузка методов.
                                            • –1
                                              Или во мне проснулся талант тонкого тролинга, или у вас что-то с нервами.
                                              • 0
                                                Мне вас жаль :)
                                                • –1
                                                  Дорогой пожалейте себя, я не нуждаюсь в жалости. Это вы тут испытываете жуткий батхерт и катаете стены текста.
                                              • +1
                                                > а) PHP поддерживает уточнение типизации при вызове методов
                                                Ну, я конечно, надеюсь, что когда нибудь можно будет использовать скалярные типы, но в данный момент это не так.

                                                > б) даже если предыдущий пункт чем-то смущает вас, то есть мнение, что сигнатуры при динамической типизации не требуют спецификации типов.
                                                Интересное мнение, и как, простите, определить, что Test::test($a) требуется параметр типа int, а Test::test($b) типа string?
                                                • –1
                                                  а) Начнём с того, что при динамической типизации скаляров смысла в матчинге по ним нет. Для этого есть олдскульные проверки is_*.

                                                  б) Смысл? Тут же все ратуют за ООП. В данный момент (и этого более чем достаточно) PHP поддерживает специфицирование параметра классом или признаком кортежа. Этого чуть более, чем достаточно.

                                                  Не делайте из сильных мест языка слабые.
                                                  • +1
                                                    Не понимаю я вас, вы сами выше написали «Мифическая невозможность сигнатурного матчинга в данном случае не аргумент, поскольку… (а) и (б)» из этого предложения мною и был сделан вывод о том что реализация этого все же возможна.

                                                    > Смысл?
                                                    Не знаю. Но это был вопрос к возможности определения требуемой сигнатуры без полноценного type hinting-а.

                                                    > Для этого есть олдскульные проверки is_*.

                                                    Есть чуть менее олдскульный оператор instanceof.

                                                    > Тут же все ратуют за ООП. В данный момент (и этого более чем достаточно) PHP поддерживает специфицирование параметра классом или признаком кортежа. Этого чуть более, чем достаточно.

                                                    Только цель у этого сейчас совсем другая.

                                                    Что касается классической перегрузки методов, то сомневаюсь что в языках с динамической типизацией в ней есть практический смысл.
                                                    • –1
                                                      Сигнатура метода в языках с динамической типизацией может не включать в себя спецификацию типа скаляров.

                                                      И это не помеха перегрузке конструкторов и методов.
                                          • +1
                                            Насколько мне известно php — интерпретируемый язык. Полагаю, определение, какую функцию вызвать можно делать в момент ее вызова, разве нет?
                                            • –2
                                              Вопрос в том КАК определить. Ваши версии?
                                    • +7
                                      Так и знал, что обязательно синглтон засунут…
                                      • +1
                                        Это же главный паттерн PHP :) Ибо при каждом запросе поднимается вся система. А чтобы лишних объектов не создавать давайте сделаем всё синглтонами :)
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                        • +4
                                          Множественное наследование есть гут. У нас, например, есть кучка полезнейших классов (Singleton, Request, Traversable, Iterator, e.t.c.) и теперь очень просто добавить разрабатываемому классу нужное поведение. Пока это приходится реализовывать через Dependency Injection.
                                          • +2
                                            Про синтаксис и механику понятно написано. Дайте больше жизненных примеров!
                                            • +1
                                              Пожалуй, это будет тема отдельного топика, но не раньше чем через полгода-год, когда я смогу с типажами поработать «в поле».
                                              • 0
                                                Полгода+год прошли, давайте уже :)
                                                • 0
                                                  За прошедшее время полностью перешёл на другие языки программирования. Так что теперь уже врядли. Но домаю найдётся кому подхватить флаг.
                                            • 0
                                              Это что-то типа Behaviors в Yii, насколько я понял.
                                              • 0
                                                Не совсем. В Yii аналог mixin.
                                              • 0
                                                Я правильно понял что это костыль вместо множественного наследования?
                                                • 0
                                                  Да, насколько я понимаю разработчики языка именно эту проблему и решали.

                                                  Конечно чистым множественным наследованием это назвать нельзя, т.к. без грязных хаков вызвать метод любого из родителей мы не можем, а сбор конечных методов для класса происходит в момент его инициализации, а не за счёт MRO, как например в python.

                                                  Тем не менее некоторые не сложные задачи, которые можно было бы решить множественным наследованием типажи покрывают.
                                                  • –2
                                                    Как ни обидно, но очередной костыль впилили вместо того чтоб сделать все «официально» и добавить таки множественное наследование, без костылей, чтоб наследуемые методы появлялись в порядке очереди.
                                                    • +2
                                                      Ни костыль. Сделано дабы избежать неоднозначности при ромбовидном наследовании.
                                                      • –1
                                                        Да Вы что? А дальше почитать там не хватило желания?
                                                        ru.wikipedia.org/wiki/Ромбовидное_наследование#.D0.A0.D0.B5.D1.88.D0.B5.D0.BD.D0.B8.D1.8F
                                                        Есть куча вариантов решения этой проблемы, вхять хотябы самый простой, который используют в питоне: берем первое вхождение.
                                                        • 0
                                                          Да вы что? А подумать, не? Неоднозначность в том, что мне приходится думать какая именно ветка наследуется.
                                                          • +1
                                                            И зачем вам ванильное множественное наследование вообще? Так просто потроллить? Трэйты абсолютно точно так же решают все проблемы.
                                                            • –1
                                                              Я не троллю, я просто не понимаю зачем делать нечто что эмулирует множественное наследование, если можно сделать множественное наследование?
                                                              «Неоднозначность в том, что мне приходится думать какая именно ветка наследуется.»
                                                              Какой класс первый в обьявлении родителей того метод и будет использоваться при совпадении, в чем проблема то? Много думать надо? Достаточно лишь посмотреть на обьявление. В других языках от этого избавились а тут не смогли бы?
                                                  • +1
                                                    Вторая – воспользоваться толи фичей, толи багой php, которая связана с использованием ключевого слова static при объявлении переменной.

                                                    Вы же используете типаж в своем последнем примере, т.е. copy-paste. Чему вы удивляетесь? Вполне ожидаемое поведение.
                                                    • 0
                                                      Такое поведение не является особенностью типажей. Если взять «чистый» класс, внутри метода написать при объявлении переменной static, затем сделать класс наследованный от первого – поведение будет аналогичным, переменная будет инициализирована отдельно в родительском классе и в дочернем.

                                                      Хуже всего, что это поведение, не документировано и на php.net я встречал комментарии, что это ошибка. А что не документировано, может быть изменено в последующем.
                                                      • +3
                                                        Давайте обратимся к документации. Вот пример оттуда:

                                                        <?php
                                                        trait Counter {
                                                            public function inc() {
                                                                static $c = 0;
                                                                $c = $c + 1;
                                                                echo "$c\n";
                                                            }
                                                        }
                                                        
                                                        class C1 {
                                                            use Counter;
                                                        }
                                                        
                                                        class C2 {
                                                            use Counter;
                                                        }
                                                        
                                                        $o = new C1(); $o->inc(); // echo 1
                                                        $p = new C2(); $p->inc(); // echo 1
                                                        


                                                        Т.е. это все-таки ожидаемое поведение, разве нет?
                                                        • +3
                                                          Спасибо, не нашёл этого ранее. Если я не ошибаюсь документацию по типажам выложили только вчера.

                                                          Теперь можно утвердать наверняка, поведение ожидаемое.
                                                        • 0
                                                          Хотя, с обычными классами — такое же поведение (проверил на 5.3.6). Удивлен.
                                                          • +2
                                                            Ничего удивительного: переменная же относится к scope функции, а не объекта. В С++ так же:
                                                            #include <iostream>
                                                            
                                                            class C {
                                                            public:
                                                                void test(int set_value = 0) {
                                                                    static int i = 0;
                                                                    if (set_value) {
                                                                        i = set_value;
                                                                    }
                                                                    std::cout << i << "\n";
                                                                }
                                                            };
                                                            
                                                            int main() {
                                                                C A, B;
                                                                A.test();
                                                                B.test();
                                                                A.test(1);
                                                                B.test();
                                                                return 0;
                                                            }
                                                            

                                                            ~/tmp$ g++ test.cpp && ./a.out
                                                            0
                                                            0
                                                            1
                                                            1
                                                            • 0
                                                              Если бы было так же — у вас было бы 0 0 1 0.
                                                              • 0
                                                                Так, секундочку, а что тогда проверяли на обычных классах, и какое там было поведение?

                                                                <?php
                                                                class C {
                                                                    public function test($set_value = null) {
                                                                        static $i = 0;
                                                                        if ($set_value) {
                                                                            $i = $set_value;
                                                                        }
                                                                        echo $i, "\n";
                                                                    }
                                                                }
                                                                $A = new C;
                                                                $B = new C;
                                                                $A->test();
                                                                $B->test();
                                                                $A->test(1);
                                                                $B->test();

                                                                ~/tmp$ php test.php #PHP 5.3.8 
                                                                0
                                                                0
                                                                1
                                                                1
                                                                • 0
                                                                  О, а с трейтами ж действительно забавно. Копипаст, точно :)

                                                                  <?php
                                                                  trait T {
                                                                      public function test($set_value = null) {
                                                                          static $i = 0;
                                                                          if ($set_value) {
                                                                              $i = $set_value;
                                                                          }
                                                                          echo $i, "\n";
                                                                      }
                                                                  }
                                                                  class A { use T; }
                                                                  class B { use T; }
                                                                  $A1 = new A;
                                                                  $A2 = new A;
                                                                  $B1 = new B;
                                                                  $B2 = new B;
                                                                  $A1->test(1);
                                                                  $B1->test(2);
                                                                  $A2->test();
                                                                  $B2->test();

                                                                  ~/tmp/php-5.4.0beta2$ ./sapi/cli/php ../test.php 
                                                                  1
                                                                  2
                                                                  1
                                                                  2
                                                                  • 0
                                                                    <?php
                                                                    class A {
                                                                        function test($set_value=0) {
                                                                            static $i = 0;
                                                                            if($set_value) {
                                                                                $i = $set_value;
                                                                            }
                                                                            echo $i.PHP_EOL;
                                                                        }
                                                                    }
                                                                    
                                                                    class B extends A {}
                                                                    class C extends A {}
                                                                    
                                                                    $b = new B();
                                                                    $c = new C();
                                                                    $b->test();
                                                                    $c->test();
                                                                    $b->test(1);
                                                                    $c->test();
                                                                    


                                                                    php test.php                #PHP 5.3.6
                                                                    0
                                                                    0
                                                                    1
                                                                    0
                                                                    
                                                                    • 0
                                                                      О, блин, как забавно. Получается, что в случае с методами это аналог статического члена класса, видимый только внутри функции. Видимо, как-то так внутри и реализовано. Особо неочевидно, кстати, если вспомнить LSB, в контексте которого ключевое слово static означает ровно противоположное. :)

                                                                      С++ с таким подходом не согласен, кстати:
                                                                      #include <iostream>
                                                                      
                                                                      class C {
                                                                      public:
                                                                          virtual void test(int set_value = 0) {
                                                                              static int i = 0;
                                                                              if (set_value) {
                                                                                  i = set_value;
                                                                              }
                                                                              std::cout << i << "\n";
                                                                          }
                                                                      };
                                                                      
                                                                      class C1 : public C {};
                                                                      
                                                                      int main() {
                                                                          C A;
                                                                          C1 B;
                                                                          A.test();
                                                                          B.test();
                                                                          A.test(1);
                                                                          B.test();
                                                                          return 0;
                                                                      }

                                                                      0
                                                                      0
                                                                      1
                                                                      1
                                                                      • +1
                                                                        Да, я этот вариант в C++ тоже проверил.
                                                                        С PHP штука в том, что переменная должна быть статичной, но она статична только в пределах текущего класса. Причем, если объявить её статичным полем класса (а не метода) — все работает как надо.
                                                        • +3
                                                          Свойства в типажах

                                                          Поразмыслил. Думается мне, что использовать их стоит только для внутренних состояний, о которых классу знать и не надо. Благо, что абстрактные методы в трейтах использовать никто не запрещает, вот, например, примитивная базовая реализация этакого минималистичного IDatasource:
                                                          <?php
                                                          
                                                          interface IDataSource {
                                                              public function has($key);
                                                              public function get($key, $default_value = null);
                                                          }
                                                          
                                                          trait TDataSource {
                                                              public function has($key) {
                                                                  return array_key_exists($key, $this->getInternalValues());
                                                              }
                                                          
                                                              public function get($key, $default_value = null) {
                                                                  return $this->has($key) ? $this->getInternalValues()[$key] : $default_value; // one more php 5.4 feature
                                                              }
                                                          
                                                              abstract protected function getInternalValues();
                                                          }
                                                          
                                                          class Storage implements IDataSource {
                                                              use TDataSource;
                                                          
                                                              protected $values = array();
                                                          
                                                              public function __construct(array $data) {
                                                                  $this->values = $data;
                                                              }
                                                          
                                                              protected function getInternalValues() {
                                                                  return $this->values;
                                                              }
                                                          }


                                                          Еще такая мысль — учитывая нестрогую и динамическую типизацию, вполне себе получается этакая своеобразная замена плюсовых темплейтов (в контексте реализации обобщенных алгоритмов).
                                                          • –8
                                                            use блиа. Раз тырили у Scala, так тырили бы уже и with, а не выдумывали новый синтаксис.
                                                            • 0
                                                              Гляжу на PHP и все больше он превращается в непонятно что, ну если хочется сделать множественное наследование, почему бы его и не сделать.
                                                              • +3
                                                                зачем оно нужно?

                                                                любитель Ruby
                                                                • 0
                                                                  А при чем тут Руби? И чем костыль в виде добавление кода препроцессором лучше множественного наследования?
                                                                  • 0
                                                                    не буду говорить про Traits, но mixin'ы в руби — средство намного более опрятное и гибкое, чем множественное наследование.
                                                                    • 0
                                                                      Не знаю как в руби, а в c++ шаблоны + множественное наследование дает обалденный результат, а в PHP в данном случае гибкости нет, просто некоторое снижение сложности разработки при кривом проектировании.

                                                                      Я к примеру не вижу плюсов в данном варианте, есть шаблон Dependency Injection, который в свою очередь даст гораздо большую гибкость.
                                                              • 0
                                                                мне кажется, что последнем примере (с синглтоном) поведение как раз правильное, так как образованный MyExtClass это новая сущность отличающаяся от MyClass. И не важно то, что она наследуется.
                                                                • 0
                                                                  Можно ли указывать trait как тип в аргументах? И есть какие-то методы определить используется ли в классе trait определенный или нет (instanceof)?
                                                                  <?php
                                                                  trait Counter {
                                                                      public function inc() {
                                                                          static $c = 0;
                                                                          $c = $c + 1;
                                                                          echo "$c\n";
                                                                      }
                                                                  }
                                                                  
                                                                  class C1 {
                                                                      public function doSomething( Counter $obj ){ ... }
                                                                  }
                                                                  
                                                                  • 0
                                                                    Для этого лучше подходят интерфейсы.
                                                                    • 0
                                                                      Вы решили поумничать? Я спросил совершенно другой вопрос, и меня именно ответ на него интересует =)
                                                                      • 0
                                                                        Нет, я высказал свое мнение о целесообразности данной фичи. А что оно не работает, выясняется запуском приведенного вами кода :)
                                                                        • 0
                                                                          Это только альфа, там многого еще нет того что есть в планах на пхп 3.4 =) Вообще хотелось бы проверять, но с другой стороны если таки сделают фичу чтоб трейты требовали интерфейсов от классов, тогда такая нужда отпадет сама собой.
                                                                    • 0
                                                                      Нет, нет.

                                                                      По второму можно только хаками, например, в каждом типаже объявлять свойство, типа $__trait_[TraitName]. Затем, вытащив массив всех свойств класса, распознать подключённые типажи (это достаточно сделать для каждого класса только один раз). Возможно что-то можно получить рефлексией.

                                                                      Типажи скорее всего не для этого создавались, возможно более красивым решением будет иметь соотвествующий каждому типажу интерфейс или запись списка типажей класса в явном виде.
                                                                      • +1
                                                                        Да, только что прочитал что можно будет указывать «обязательное» наличие интерфейса в классе который использует данный типаж =)
                                                                        trait IteratorUser require Iterator {} // как-то так
                                                                    • 0
                                                                      Пример от Knp Labs с деревьями knplabs.com/blog/csi/php-materialized-path-tree
                                                                      • 0
                                                                        Кстати, о трейтах.
                                                                        Вот тут переводчики документации обсуждали, как правильно перевести «trait»: news.php.net/php.doc.ru/2908
                                                                        Я со своей высокой башни влез и сказал, что traits они и есть трейты. Возможно, был не прав.
                                                                        Если у кого-то есть обоснованные возражения на эту тему — скажите им, можно прямо в этом треде, Irker, я вижу, тут в комментах есть.
                                                                        • 0
                                                                          Транслитерация английских слов не улучшает понимание.
                                                                          Если если русское соответствие, то почему его не использовать?
                                                                          «Типаж» достаточно точно передает смысл слова «trait».
                                                                          Тем более если это термин уже используется в программировании.
                                                                        • 0
                                                                          Можно ли использовать динамическое подключение типу function a() { use B; } и будет ли доступен типаж, подключенный в предке?
                                                                          • 0
                                                                            Эти типажи очень похожи на дельфовые интерфейсы.

                                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.