I need to initialize the trait property as an instance of the neighboring class.

trait FilterTrait { protected $_filter = new Filter(); // Нельзя использовать как дефолтное значение public function __construct () { // Перезапишется в используемом классе $this->_filter = new Filter(); } public function SetFilter ($arFilter) { $this->_filter->Set($arFilter); } public function CleanFilter () { $this->_filter->CLean(); } public function GetFilter () { return $this->_filter->Get(); } ... } 

I do not want a child class to help with the work of the used treit, and generally know something about its device, like this:

 class Select extends Query { use TablesListTrait, FieldsListTrait, FilterTrait, HavingTrait, SortTrait, LimitTrait, GroupTrait; function __construct () { $this->_tablesList = new TablesList(); $this->_fieldsList = new FieldsList(); $this->_filter = new Filter(); ... } ... } 

And I also don't want to initialize each time the method is called

 trait FilterTrait { protected $_filter; public function InitializeFilter () { if (is_null($this->_filter)) { $this->_filter = new Filter(); } } public function SetFilter ($arFilter) { $this->InitializeFilter(); $this->_filter->Set($arFilter); } public function CleanFilter () { $this->InitializeFilter(); $this->_filter->CLean(); } public function GetFilter () { $this->InitializeFilter(); return $this->_filter->Get(); } } 

You can, of course, stir up reloading methods, but this is not much better.

Found information that in version 5.6 here such writing is supported

 class Foo { protected $bar = new Baz(); } 

I did not check it, but still, most of the hosting sites currently stand at 5.3 - 5.4, and it’s still difficult to use these chips. Besides, I also want to implement the registration of mnemonics for calling the trait methods from the parameters passed to the child classes. If there was any magic method that registers several __construct functions, it would help me:

 trait Actions { protected $_arActions = array(); public function RegisterActions ($arActions) { $this->_arActions = array_merge($this->_arActions, $arActions); } public function ResetParameters ($arParameters) { foreach ($arParameters as $mnemonic => $actionParameters) { if (isset($this->_arActions[$mnemonic])) { $action = $this->_arActions[$mnemonic]; $this->$action($actionParameters); } } } } trait FilterTrait { use Actions; public function __onUse () { $arActions = array( "filter" => "SetFilter" ); $this->RegisterActions($arActions); } public function SetFilter ($arFilter) {...} } class Select extends Query { use TablesListTrait, FieldsListTrait, FilterTrait, HavingTrait, SortTrait, LimitTrait, GroupTrait; public function __construct ($arParams) { $this->ResetParameters($arParams); } } ... $arSelectParams = array( "table" => "products", "fields" => array( "id", "name", "price" ), "filter" => array( "name" => "%iphone%", "<=price" => 15000 ), "sort" => array( "price" => "asc" ) ); $query = new Select($arSelectParams); 

Is there any option to do something like this now or, if not, is the concept of such a thing expected in the future, somewhere in the seventh version?

  • one
    That is why multiple inheritance is not used. Traits as mixins have no right to initialization, because they only bring additional functionality and do not take part in the construction of the object. If you need to implement a set of classes with some functionality and the same external access, then you should use just an array of implementations of a single interface. - etki

1 answer 1

The main idea of ​​the traits is to allow some behavior to be added to the class. Trait itself is not a full-fledged entity, therefore the use of __construct in it is meaningless from the point of view of a strict Object-Oriented Approach.

What you want to do is more likely not multiple inheritance than impurities. And yes, there is no multiple inheritance in PHP.

If we talk about the initialization of properties, then I would advise you to initialize the trait fields only when it is needed. In the question you bring a piece of code, with the initialization of the property in each method, adding:

And I also don't want to initialize each time the method is called

The problem is that you incorrectly implement on-demand initialization. The correct solution would be to use getters / setters to get the properties and implement the initialization logic in them. For example:

 trait FilterTrait { protected $_filter; public function setFilter ($arFilter) { $this->_filter = $arFilter; } public function getFilter () { if (is_null($this->_filter)) { $this->_filter = new Filter(); } return $this->_filter; } public function cleanFilter () { $this->getFilter()->clean(); } public function doSomethig() { $this->getFilter()->doSomething(); } } 

Regarding your last example, I would recommend using setters for query parameters (filters, sorts, ...) explicitly, rather than using configuration magic through the constructor. Doctrine DBAL and other similar libraries function in a similar way. This is how an example query construction might look like:

 $queryBuilder ->select('id', 'name') ->from('users') ->where('email = ?') ->setParameter(0, $user_email); 

This example uses two well-known design patterns: Builder and Fluent Interface . I think it will be useful for you to get acquainted with these and other design patterns.