匿名类

当需要创建简单的一次性对象时,匿名类非常有用。

<?php

// 使用显式类
class Logger
{
public function
log($msg)
{
echo
$msg;
}
}

$util->setLogger(new Logger());

// 使用匿名类
$util->setLogger(new class {
public function
log($msg)
{
echo
$msg;
}
});

它们可以像普通类一样向构造函数传递参数、扩展其他类、实现接口以及使用特性。

<?php

class SomeClass {}
interface
SomeInterface {}
trait
SomeTrait {}

var_dump(new class(10) extends SomeClass implements SomeInterface {
private
$num;

public function
__construct($num)
{
$this->num = $num;
}

use
SomeTrait;
});

上面的例子将输出

object(class@anonymous)#1 (1) {
  ["Command line code0x104c5b612":"class@anonymous":private]=>
  int(10)
}

在另一个类中嵌套匿名类不会使其访问该外部类的任何私有或受保护的方法或属性。为了使用外部类的受保护属性或方法,匿名类可以扩展外部类。为了在匿名类中使用外部类的私有属性,必须通过其构造函数传递它们。

<?php

class Outer
{
private
$prop = 1;
protected
$prop2 = 2;

protected function
func1()
{
return
3;
}

public function
func2()
{
return new class(
$this->prop) extends Outer {
private
$prop3;

public function
__construct($prop)
{
$this->prop3 = $prop;
}

public function
func3()
{
return
$this->prop2 + $this->prop3 + $this->func1();
}
};
}
}

echo (new
Outer)->func2()->func3();

上面的例子将输出

6

由同一个匿名类声明创建的所有对象都是该类的实例。

<?php
function anonymous_class()
{
return new class {};
}

if (
get_class(anonymous_class()) === get_class(anonymous_class())) {
echo
'same class';
} else {
echo
'different class';
}

上面的例子将输出

same class

注意:

请注意,匿名类由引擎分配一个名称,如以下示例所示。此名称应被视为实现细节,不应依赖于它。

<?php
echo get_class(new class {});

上面的例子将输出类似于

class@anonymous/in/oNi1A0x7f8636ad2021

添加笔记

用户贡献笔记 9 笔记

Anonymous
8 年前
以下三个例子描述了匿名类,它们非常简单和基础,但很容易理解。

<?php
// 第一种方式 - 匿名类直接赋值给变量
$ano_class_obj = new class{
public
$prop1 = 'hello';
public
$prop2 = 754;
const
SETT = 'some config';

public function
getValue()
{
// 执行一些操作
return 'some returned value';
}

public function
getValueWithArgu($str)
{
// 执行一些操作
return 'returned value is '.$str;
}
};

echo
"\n";

var_dump($ano_class_obj);
echo
"\n";

echo
$ano_class_obj->prop1;
echo
"\n";

echo
$ano_class_obj->prop2;
echo
"\n";

echo
$ano_class_obj::SETT;
echo
"\n";

echo
$ano_class_obj->getValue();
echo
"\n";

echo
$ano_class_obj->getValueWithArgu('OOP');
echo
"\n";

echo
"\n";

// 第二种方式 - 匿名类通过定义的函数赋值给变量
$ano_class_obj_with_func = ano_func();

function
ano_func()
{
return new class {
public
$prop1 = 'hello';
public
$prop2 = 754;
const
SETT = 'some config';

public function
getValue()
{
// 执行一些操作
return 'some returned value';
}

public function
getValueWithArgu($str)
{
// 执行一些操作
return 'returned value is '.$str;
}
};
}

echo
"\n";

var_dump($ano_class_obj_with_func);
echo
"\n";

echo
$ano_class_obj_with_func->prop1;
echo
"\n";

echo
$ano_class_obj_with_func->prop2;
echo
"\n";

echo
$ano_class_obj_with_func::SETT;
echo
"\n";

echo
$ano_class_obj_with_func->getValue();
echo
"\n";

echo
$ano_class_obj_with_func->getValueWithArgu('OOP');
echo
"\n";

echo
"\n";

// 第三种方式 - 通过构造函数将参数传递给匿名类
$arg = 1; // 我们通过一些操作获取它
$config = [2, false]; // 我们通过一些操作获取它
$ano_class_obj_with_arg = ano_func_with_arg($arg, $config);

function
ano_func_with_arg($arg, $config)
{
return new class(
$arg, $config) {
public
$prop1 = 'hello';
public
$prop2 = 754;
public
$prop3, $config;
const
SETT = 'some config';

public function
__construct($arg, $config)
{
$this->prop3 = $arg;
$this->config =$config;
}

public function
getValue()
{
// 执行一些操作
return 'some returned value';
}

public function
getValueWithArgu($str)
{
// 执行一些操作
return 'returned value is '.$str;
}
};
}

echo
"\n";

var_dump($ano_class_obj_with_arg);
echo
"\n";

echo
$ano_class_obj_with_arg->prop1;
echo
"\n";

echo
$ano_class_obj_with_arg->prop2;
echo
"\n";

echo
$ano_class_obj_with_arg::SETT;
echo
"\n";

echo
$ano_class_obj_with_arg->getValue();
echo
"\n";

echo
$ano_class_obj_with_arg->getValueWithArgu('OOP');
echo
"\n";

echo
"\n";
ytubeshareit at gmail dot com
7 年前
匿名类是语法糖,对某些人来说可能看起来很欺骗性。
“匿名”类仍然会被解析到全局作用域,在那里它会被自动分配一个名称,并且每当需要该类时,就会使用该全局类定义。示例说明...

匿名类版本...
<?php

function return_anon(){
return new class{
public static
$str="foo";
};
}
$test=return_anon();
echo
$test::$str; // 输出 foo

// 我们仍然可以在全局作用域中直接访问 “anon” 类!
$another=get_class($test); // 获取自动分配的名称
echo $another::$str; // 输出 foo
?>

上面的代码功能上等同于这样操作....
<?php
class I_named_this_one{
public static
$str="foo";
}
function
return_not_anon(){
return
'I_named_this_one';
}
$clzz=return_not_anon();// 获取类名
echo $clzz::$str;
?>
sebastian.wasser at gmail
5 年前
我想分享一下我对匿名类的静态属性的发现。

所以,给定一个像这样的生成匿名类对象的函数

<?php
function nc () {
return new class {
public static
$prop = [];
};
}
?>

获取一个新对象并修改静态属性

<?php
$a
= nc();
$a::$prop[] = 'a';

var_dump($a::$prop);
// array(1) {
// [0] =>
// string(1) "a"
// }
?>

现在获取另一个对象并修改静态属性会改变原始对象,这意味着静态属性确实是静态的

<?php
$b
= nc();
$b::$prop[] = 'b';

var_dump($b::$prop); // 与 var_dump($a::$prop); 相同
// array(2) {
// [0] =>
// string(1) "a"
// [1] =>
// string(1) "b"
// }

assert($a::$prop === $b::$prop); // true
?>
joey
5 年前
似乎唯一能对它进行类型提示的方式是作为 object。

如果你需要在一个函数中使用匿名类的多个实例,你可以使用

$class = function(string $arg):object {
return new class($arg) {
public function __construct(string $arg) {
$this->ow = $arg;
}
};
};

尽管为了结构的缘故,不建议在单一作用域之外或者跨多个文件使用这种方式。但是,如果你的类只在一个作用域中使用,那么它可能不是一个代码混乱的问题。
j.m \ jamesweb \ ca
6 年前
/* 我喜欢 OneShot 类这个想法。
感谢这位匿名兄弟\姐妹的精确说明
new class( $a, $b )
¯¯¯¯¯¯¯¯¯

如果你出于任何原因(例如:以可读方式加载文件,而不使用自动加载)需要 “延迟 OneShot 匿名类”,它可能看起来像这样; */

$u = function()use(&$u){
$u = new class{private $name = 'Utils';};
};

$w = function(&$rewrite)use(&$w){
$w = null;
$rewrite = new class{private $name = 'DataUtils';};
};

// 用法;
var_dump(
array(
'延迟',
'( 自毁 )',
'匿名类创建',
array(
'之前 ( $u )' => $u,
'运行 ( $u() )' => $u(),
'之后 ( $u )' => $u,
),
0,0,
0,0,
0,0,
'延迟',
'( 覆盖 && 自毁 )',
'匿名类创建',
array(
'之前 ( $w )' => $w,
'运行 ( $w($u) )' => $w($u),
'之后 ( $w )' => $w,
'之后 ( $u )' => $u
)
)
);

// 顺便说一下:糟糕,我失败了一个垃圾邮件挑战
razvan_bc at yahoo dot com
4 年前
你可以尝试这些

<?php

$oracle
=&$_['nice_php'];
$_['nice_php']=(function(){
return new class{
public static function
say($msg){
echo
$msg;
}

public static function
sp(){
echo
self::say(' ');
}

};
});

/*
$_['nice_php']()::say('Hello');
$_['nice_php']()::sp();
$_['nice_php']()::say('World');
$_['nice_php']()::sp();
$_['nice_php']()::say('!');
//几乎相同的代码在底部
*/

$oracle()::say('Hello');
$oracle()::sp();
$oracle()::say('World');
$oracle()::sp();
$oracle()::say('!');
?>
piotr at maslosoft dot com
7 年前
请注意,`get_class` 返回的类名可能包含空字节,就像我的 PHP 版本 (7.1.4) 中一样。

当类起始行或其主体发生变化时,名称将更改。

是的,名称是实现细节,不应依赖,但在一些罕见的情况下需要它(注释匿名类)。
solobot
6 年前
eval() 是在循环中生成多个具有静态属性的匿名类的解决方法
<?php
public function generateClassMap()
{
foreach (
$this->classMap as $tableName => $class)
{
$c = null;
eval(
'$c = new class extends \common\MyStaticClass {
public static $tableName;
public static function tableName()
{
return static::$tableName;
}
};'
);
$c::$tableName = $this->replicationPrefix.$tableName;
$this->classMap[$tableName] = $c;

}
}
?>
因此每个类将拥有自己的 $tableName 而不是公共祖先。
primipilus13 at gmail dot com
7 年前
<?php

// 使用构造函数和扩展在匿名类中

class A
{
private
$name;

public function
__construct($name)
{
$this->name = $name;
}

public function
getName()
{
return
$this->name;
}
}

$b = new class('anonymous') extends A
{
public function
getName()
{
return
parent::getName() . ' class';
}
};

echo
$b->getName(), PHP_EOL;

// 结果:匿名类
To Top