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);