<rmcreative>

RSS

traits и члены класса

1 октября 2011

traits в PHP 5.4 — довольно занятный механизм. Хотя, приведённое ниже в официальном мануале не показывается (возможно, решили перестраховаться), но работает:

trait MyTrait
{
    public $traitVar = 'trait var';
 
    public function test()
    {
        echo $this->traitVar;
        echo $this->classVar;
    }
}
 
class MyClass
{
    private $classVar = 'class var';
    use MyTrait;
}
 
$c = new MyClass();
$c->test();
echo $c->traitVar;

Комментарии RSS

  1. №5432
    aktuba
    aktuba 01 окт. 2011 г., 2:22:29

    Подробнее можешь объяснить, что это и для чего?

  2. №5433
    Андрей Геоня
    Андрей Геоня 01 окт. 2011 г., 2:37:23

    @aktuba С помощью данного механизма можно "подмешивать" функциональность из одного класса в другой, таким образом можно предотвратить построение громоздких (и не всегда удобных) иерархий классов и "горизонтально" наращивать функциональность.

  3. №5434
    OZ
    OZ 01 окт. 2011 г., 10:47:15

    У Trait не может быть переменных класса.

  4. №5435
    OZ
    OZ 01 окт. 2011 г., 10:47:47

    Я имею ввиду public $traitVar = 'trait var';

  5. №5436
    Spider
    Spider 01 окт. 2011 г., 12:05:42

    2OZ: это почему это?

  6. №5437
    Spider
    Spider 01 окт. 2011 г., 12:13:52

    А почему это не должно работать? Разработчики писали, что traits — это что-то вроде include, т.е. фактически весь их код подставляется прямо в класс при компиляции

  7. №5438
    Максим
    Максим 01 окт. 2011 г., 12:39:18

    я так понимаю это аналог behaviors ?

  8. №5439
    OZ
    OZ 01 окт. 2011 г., 15:17:11

    Spider, чтобы небыло конфликтов с переменными класса. Подключаться может только поведение, то есть методы. Но не сущности.

  9. №5440
    Sam
    Sam 02 окт. 2011 г., 2:04:01

    Максим, не совсем. Есть существенные отличия. Behavior более функционален в плане реюза кода, поэтому из Yii2 мы их не выпилим точно.

    OZ, приведённый выше код отлично работает на бете 5.4.

  10. №5441
    OZ
    OZ 02 окт. 2011 г., 2:25:21

    Как бы то ни было, это не правильно и это не будет работать в релизе. Traits с переменными класса ничем не отличаются от обычных классов. Сборник методов не может иметь своих полей. Trait это сборник методов - не больше.

  11. №5442
    Sam
    Sam 02 окт. 2011 г., 2:32:26

    OZ, ещё как отличается. Переменные в трейте — это переменные класса, к которому трейт цепляется, а не переменные самого трейта. У трейта нет состояния, нет экземпляра.

  12. №5443
    OZ
    OZ 02 окт. 2011 г., 2:40:01

    Trait не знает, к какому классу он будет добавлен. Trait это набор методов, а не сущность и не статический класс, поэтому само объявление переменной внутри Trait лишено логики.

  13. №5444
    Sam
    Sam 02 окт. 2011 г., 3:03:06

    OZ, мы фактически об одном и том же говорим. Про «перестраховаться» я не зря писал. Возможно, в релизе и уберут. А может и нет…

  14. №5446
    White-Shadow
    White-Shadow 02 окт. 2011 г., 11:52:45

    само объявление переменной внутри Trait лишено логики.

    Почему? методы же должны где-то хранить состояния того что они делают? иначе было бы достаточно статического класса или функции чтобы что-то делать, зачем это в класс примешивать? Поэтому $traitVar тут как раз к месту, а вот $classVar из trait - да не хорошо...

    если не убедил вот пример:

    trait Timestampable {
        private $_created;
        private $_updated;
     
        public function getCreationDate() {
            return $this->_created;
        }
     
        public function setCreationDate($date) {
            $this->_created = $date;
        }
     
        /* тоже для Update +- в зависимости от orm метод который ставит дефолтовые значения */
    }
     
     
    class BlogPost {
      use Timestampable;
    }

    Если в trait нельзя объявлять переменные, то зачем он такой нужен в классе? Достаточно интерфейса и немного копипасты... В любом случае $this->classVar из trait будет работать, даже если захотят - не так просто это запретить.

    ну если переменные уберут - можно так, но первый вариант как-то лучше смотрится.

    trait Timestampable {
     
        public function getCreationDate() {
            return $this->setCreationDate(null);
        }
     
        public function setCreationDate($date) {
            static $created = new \DateTime('now');
     
            if (null === $date) {
                 return $created;
            } else {
                 $created = $date;
            }
        }
     
    }
  15. №5447
    OZ
    OZ 02 окт. 2011 г., 11:56:14

    White-Shadow, нет, методы не должны хранить своё состояние, потому что Trait это не объект, у него нет состояния. "и немного копипасты..." - чтобы эту грязь убрать, и были созданы traits.

  16. №5448
    White-Shadow
    White-Shadow 02 окт. 2011 г., 12:52:33

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

    то есть Trait - это законченный кирпичик для класса (поведение) которые требуется и другим класса, собственно кроме ActiveRecord - мне примеры и не приходят в голову. Еще разве что синглтоны, фабрики и т.п. кстати self в trait - класс куда его примешали...

  17. №5452
    OZ
    OZ 02 окт. 2011 г., 13:10:56

    White-Shadow, Trait может пользоваться переменными класса, в который его примешивают, но не должен вносить переменные. В trait нет инструмента разрешения конфликтов имён переменных (а конфликтов методов - есть).

  18. №5453
    Максим
    Максим 02 окт. 2011 г., 13:51:15

    Помечтаем :) :

    behavior Timestampable {
        private $_created;
        private $_updated;
     
        public function getCreationDate() {
            return $this->_created;
        }
     
        public function setCreationDate($date) {
            $this->_created = $date;
        }
     
        /* тоже для Update +- в зависимости от orm метод который ставит дефолтовые значения */
    }
     
     
    class BlogPost {
     behavior Timestampable;
    }
  19. №5455
    Sam
    Sam 02 окт. 2011 г., 14:39:47

    В общем-то это в Yii и сейчас есть.

  20. №5458
    White-Shadow
    White-Shadow 02 окт. 2011 г., 17:45:19

    Sam

    а что показывает Reflection MyClass'a?

  21. №5459
    Sam
    Sam 03 окт. 2011 г., 0:28:30

    White-Shadow, для trait-а:

    object(ReflectionClass)#3 (1) {
      ["name"]=>string(19) "EventAddingBehavior"
    }

    Для метода trait-а:

    &object(ReflectionMethod)#4 (2) {
      ["name"]=>string(4) "test"
      ["class"]=>string(19) "EventAddingBehavior"
    }

    Ну и из класса, естественно, трейты можно получить через ReflectionClass::getTraits.

  22. №5474
    Igor
    Igor 05 окт. 2011 г., 18:54:45

    вот же есть RFC, в котором написано как оно планировалось, что будет работать: https://wiki.php.net/rfc/traits

  23. №5475
    Sam
    Sam 05 окт. 2011 г., 20:19:11

    Igor, RFC не обязательно соответствуют действительности. Тот, на который вы ссылаетесь, написан в 2008-м. Первый патч, добавляющий трейты сделан в начале 2009-го. Два года всё-таки прошло.

  24. №5476
    Igor
    Igor 06 окт. 2011 г., 9:13:46

    Да, он был заменен на "Horizontal Reuse" https://wiki.php.net/rfc/horizontalreuse Который был обновлен в этом году.

  25. №5477
    OZ
    OZ 06 окт. 2011 г., 9:45:47

    Там написано, что при любом использовании свойств будет генерироваться E_STRICT (1), а при несоответствии объявления будет fatal error. Я не согласен с тем, что про такой вариант можно сказать "работает".

    (1) In all other cases, i.e., when the definitions are identical, an E_STRICT notice is shown to raise awareness about the potentially problematic, and discouraged use of properties.

  26. №5478
    Sam
    Sam 06 окт. 2011 г., 17:32:32

    OZ, я вообще не очень согласен с тем, что это trait, а не полноценный mixin.

  27. №5480
    OZ
    OZ 07 окт. 2011 г., 18:48:06

    Кстати, если лень ставить php 5.4, чтобы экспериментировать, можно воспользоваться вот этим сервисом: http://codepad.viper-7.com/kadPmP

  28. №7811
    DropSQL
    DropSQL 13 апр. 2013 г., 22:46:23

    ru2.php.net/manual/en/language.oop5.traits.php

    Properties

    <?php
    trait PropertiesTrait {
        public $x = 1;
    }
     
    class PropertiesExample {
        use PropertiesTrait;
    }
     
    $example = new PropertiesExample;
    $example->x;
    ?>

    Properties conflict

    <?php
    trait PropertiesTrait {
        public $same = true;
        public $different = false;
    }
     
    class PropertiesExample {
        use PropertiesTrait;
        public $same = true; // Strict Standards
        public $different = true; // Fatal error
    }
    ?>

    Собственно уже могут быть, так что пункт 2 комментария №5986 можно отказаться, мне так кажется :)

  1. Почта опубликована не будет.

  2. Можно использовать синтаксис Markdown или HTML.

  3. Введите ответ в поле. Щёлкните, чтобы получить другую задачу.