PHP:阻止在 class 之外创建实例变量
PHP: deterring instance variable creation outside the class
在下面给定的 PHP class 中,我了解到 $test 将无法从 class.
外部访问
class Resource {
private $test;
public function __construct() {
echo "in init";
$test = "new val";
}
}
但是我们将能够定义新的实例变量,如下所示。有什么技巧可以阻止它吗?
$r = new Resource();
$r->val = "value";
使用魔术方法(namly __set
)你可以告诉 class "if this is not set in here, ignore it",例如;
<?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
参考请看the guide
将数据写入不可访问的属性时调用 'magic method' __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/Whosebug/so-tmp-f2.php:20 Stack trace: #0 /var/www/Whosebug/so-tmp-f2.php(26): Foo->__set('qux', 'earth') #`1 {main} thrown in /var/www/Whosebug/so-tmp-f2.php on line 20
如您所见,当尝试写入未声明的 属性.
时,上面的代码会抛出异常
如果您尝试写入另一个不可访问的 属性(在类似范围内),如上面的 Foo::baz
(声明为私有),调用将通过 __set 魔法路由法也。如果没有 __set 方法,这将导致致命错误。
但是,对 Foo::bar
的写入(上面声明的 public)将不会通过 __set 魔术方法进行路由。
如果你想在许多 类 中强制执行这种类型的严格行为,你可以将以上内容作为特征来实现:
trait Upsetter
{
public function __set($name, $value)
{
throw new Exception('Strictly no writes to inaccesible and undeclared properties.');
}
}
class Bob
{
use Upsetter;
}
相当安全的解决方案。您应该避免使用那些 __set 方法,因为它们不关心 private/protected 属性。使用 class 反射,查看 属性 是否为 public 并且可用于 __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;
在下面给定的 PHP class 中,我了解到 $test 将无法从 class.
外部访问class Resource {
private $test;
public function __construct() {
echo "in init";
$test = "new val";
}
}
但是我们将能够定义新的实例变量,如下所示。有什么技巧可以阻止它吗?
$r = new Resource();
$r->val = "value";
使用魔术方法(namly __set
)你可以告诉 class "if this is not set in here, ignore it",例如;
<?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
参考请看the guide
将数据写入不可访问的属性时调用 'magic method' __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/Whosebug/so-tmp-f2.php:20 Stack trace: #0 /var/www/Whosebug/so-tmp-f2.php(26): Foo->__set('qux', 'earth') #`1 {main} thrown in /var/www/Whosebug/so-tmp-f2.php on line 20
如您所见,当尝试写入未声明的 属性.
时,上面的代码会抛出异常如果您尝试写入另一个不可访问的 属性(在类似范围内),如上面的 Foo::baz
(声明为私有),调用将通过 __set 魔法路由法也。如果没有 __set 方法,这将导致致命错误。
但是,对 Foo::bar
的写入(上面声明的 public)将不会通过 __set 魔术方法进行路由。
如果你想在许多 类 中强制执行这种类型的严格行为,你可以将以上内容作为特征来实现:
trait Upsetter
{
public function __set($name, $value)
{
throw new Exception('Strictly no writes to inaccesible and undeclared properties.');
}
}
class Bob
{
use Upsetter;
}
相当安全的解决方案。您应该避免使用那些 __set 方法,因为它们不关心 private/protected 属性。使用 class 反射,查看 属性 是否为 public 并且可用于 __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;