В приведенном ниже PHP-классе я понимаю, что $ test не будет доступен извне этого класса.
class Resource {
private $test;
public function __construct() {
echo "in init";
$test = "new val";
}
}
Но мы сможем определить новые переменные экземпляра, как показано ниже. Есть ли хитрость, чтобы остановить это?
$r = new Resource();
$r->val = "value";
Используя магические методы (Namly __set
) вы можете сказать классу «если это не задано здесь, игнорировать его», например;
<?php
class Resource {
private $test;
public function __construct() {
echo "in init";
$this->test = "new val";
}
public function __set($name, $val)
{
// If this variable is in the class, we want to be able to set it
if (isset($this->{$name})
{
$this->{$name} = $val;
}
else
{
// Do nothing, add an error, anything really
}
}
}
$resource = new Resource();
$resource->foo = "bar";
echo $resource->foo; // Will print nothing
Для справки, пожалуйста, смотрите гид
Довольно безопасное решение. Вам следует избегать использования этих методов __set, потому что они не заботятся о закрытых / защищенных свойствах. Используйте отражение класса, чтобы увидеть, является ли свойство общедоступным и доступным для __set. Небольшой пример ниже.
<?php
class Resource {
private $test = 55;
public $foo;
public $bar;
public function __set($name, $value)
{
if(isset($this->{$name})) {
$reflection = new ReflectionObject($this);
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$isPublic = false;
/** @var ReflectionProperty $property */
foreach ($properties as $property) {
if ($property->getName() == $name) {
$isPublic = true;
break;
}
}
if ($isPublic) {
$this->{$name} = $value;
} else {
//is private, do not set
echo 'Im private, dont touch me!';
}
} else {
// not here
echo 'Im not here';
}
}
}
$resource = new Resource();
$resource->test = 45;
«Волшебный метод» __set
(если объявлено) вызывается при записи данных в недоступные свойства.
Вы можете использовать это в своих интересах, чтобы предотвратить создание необъявленных свойств:
<?php
class Foo
{
public $bar;
private $baz;
public function __set($name, $value)
{
throw new Exception('Strictly no writes to inaccesible and undeclared properties.');
}
}
$f = new Foo;
$f->bar = 'hello';
$f->qux = 'earth';
Выход:
Fatal error: Uncaught Exception: Strictly no writes to inaccesible and undeclared properties. in /var/www/stackoverflow/so-tmp-f2.php:20 Stack trace: #0 /var/www/stackoverflow/so-tmp-f2.php(26): Foo->__set('qux', 'earth') #`1 {main} thrown in /var/www/stackoverflow/so-tmp-f2.php on line 20
Как вы можете видеть, вышеприведенное вызовет исключение при попытке записи в необъявленное свойство.
Если вы попытаетесь написать в другое недоступное свойство (в аналогичном объеме), как Foo::baz
выше (который объявлен как private), вызовы будут также перенаправляться через магический метод __set. Без метода __set это приведет к фатальной ошибке.
Тем не менее, написать Foo::bar
(объявлено публично выше) НЕ будет маршрутизироваться через магический метод __set.
Если вы хотите применить этот тип строгого поведения во многих классах, вы можете реализовать это как черту:
trait Upsetter
{
public function __set($name, $value)
{
throw new Exception('Strictly no writes to inaccesible and undeclared properties.');
}
}
class Bob
{
use Upsetter;
}