PHP Properties
, (*1)
PHP Properties implementations based on getters or setters method and used
PSR-5 PHPDoc information., (*2)
Installation
composer require serafim/properties, (*3)
Package on packagist.org, (*4)
Introduction
Properties provide the ability to control the behavior of setting and
retrieving data from an object fields., (*5)
There are no properties at the language level, but they can be implemented with
the some additional functionality., (*6)
For example:, (*7)
class MyClass
{
protected $a = 23;
public function __get($field)
{
return $this->$field;
}
}
$dto = new MyClass();
echo $dto->a; // 23
$dto->a = 42; // Cannot access protected property MyClass::$a
This code example does not provide type-hint and autocomplete.
Using magic without the ability to control it is always a bad option.
...well, it looks awful %), (*8)
We can fix some problems using PSR-5. In this case, we can add a docblock., (*9)
/**
* @property-read int $a
*/
class MyClass
{
// ...
But this docblock only adds information for the IDE and does not
affect the code itself. At the same time, you should always keep it up to date., (*10)
But wait! We have another problem., (*11)
$dto = new MyClass();
echo isset($dto->a); // false
This is obviously a bug. We still need to add __isset, __unset and __set support., (*12)
It's damn awful!!111, (*13)
Properties Usage
Now let's see what the serafim/properties package provides., (*14)
/**
* @property $a
*/
class MyClass
{
use Serafim\Properties\Properties;
protected $a = 23;
}
$dto = new MyClass();
$dto->a; // 23
$dto->a = 42; // Ok
$dto->a; // 42
Readonly Properties
To indicate that the type should be readonly use @property-read annotation., (*15)
/**
* @property-read $a
*/
class MyClass
{
use Serafim\Properties\Properties;
protected $a = 23;
}
$dto = new MyClass();
$dto->a; // 23
$dto->a = 42; // Error: Property MyClass::$a is readonly
Writeonly Properties
To indicate that the type should be readonly use @property-write annotation., (*16)
/**
* @property-write $a
*/
class MyClass
{
use Serafim\Properties\Properties;
protected $a = 23;
}
$dto = new MyClass();
$dto->a = 42; // 42
$dto->a; // Error: Property MyClass::$a is writeonly
Getters And Setters
For getter or setter override just declare get[Property]
(is[Property] for bool values will be works too) or set[Property] methods., (*17)
/**
* @property-read int $a
*/
class MyClass
{
use Serafim\Properties\Properties;
protected $a = 23;
protected function getA()
{
return $this->a + 19;
}
}
$dto = new MyClass();
echo $dto->a; // 42 (because 23 + 19 = 42)
$dto->a = 42; // Error: Property is read-only (@property-read doc declaration)
Setter:, (*18)
/**
* @property-write string $anotherProperty
*/
class Some
{
// ...
protected $anotherProperty = 'some';
/**
* @param string $newVal
*/
public function setAnotherProperty($newVal)
{
// Just example
if (mb_strlen($newVal) > 4) {
throw new InvalidArgumentException('...');
}
$this->anotherProperty = $newVal;
}
}
Autocomplete
All these annotations fully work in the IDE, including
autocomplete and highlighting incorrect behavior., (*19)
, (*20)
Type Hints
All properties with writeable behavior will be "type checkable"., (*21)
/**
* @property int|null $a
*/
class Some
{
use Serafim\Properties\Properties;
protected $a;
}
//
$some = new Some;
$some->a = 23; // Ok
$some->a = null; // Ok
$some->a = 'string'; // Error: "TypeError: Value for property Some::$a must be of the type int|null, string given"
Primitive Type Hints
-
int (or integer) - property value are integer
-
bool (or boolean) - value are boolean
-
float (or double) - value are float
-
string - value are string or object with __toString method
-
null (or void) - value are nullable
-
resource - value are resource
-
object - value can be any object
-
mixed - no type checking
-
callable - value can be string, instance of \Closure, array with 2 args or object with __invoke method
-
scalar - value cannot be an object
-
countable - value can be a countable (array or object provided Countable interface).
-
self - value can be object of self class or string with name of self class, (*22)
self keyword does not available yet: it will be supports in future, (*23)
-
static - value can be instance of self class or string whos are sublass of self, (*24)
static keyword does not available yet: it will be supports in future, (*25)
-
$this - value can be only object instance of self class, (*26)
$this keyword does not available yet: it will be supports in future, (*27)
Arrays And Generics
-
array - value is type of array
-
Class[] - value is type of array or instance of \Traversable
-
scalar[] - value is type of array or instance of \Traversable
-
Collection<> - value is type of array or instance of "Collection" and \Traversable
-
Collection<T> - value is type of array or instance of "Collection" and \Traversable
-
Collection<T,V>- value is type of array or instance of "Collection" and \Traversable
Conjunction And Disjunction
-
a|b - means that the value must be type (a or b).
-
a&b - means that the value must be type (a and b).
-
a|b&c - means that the value must be type (a or (b and c)).
See more: https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#appendix-a-types, (*28)
Production Mode
The code is quite effective, but in the production mode you
should use caching. The package implements support for the PSR-16 standard., (*29)
$driver = new Psr16CacheDriver(); // Your PSR16 cache driver implementation
$properties = Serafim\Properties\Bootstrap::getInstance();
$properties->setCacheDriver($driver);