匿名类

PHP 7 开始支持匿名类。 匿名类很有用,可以创建一次性的简单对象。

<?php

// PHP 7 之前的代码
class Logger
{
    public function 
log($msg)
    {
        echo 
$msg;
    }
}

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

// 使用了 PHP 7+ 后的代码
$util->setLogger(new class {
    public function 
log($msg)
    {
        echo 
$msg;
    }
});

可以传递参数到匿名类的构造器,也可以扩展(extend)其他类、实现接口(implement interface),以及像其他普通的类一样使用 trait:

<?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)
}

匿名类被嵌套进普通 Class 后,不能访问这个外部类(Outer class)的 private(私有)、protected(受保护)方法或者属性。 为了访问外部类(Outer class)protected 属性或方法,匿名类可以 extend(扩展)此外部类。 为了使用外部类(Outer class)的 private 属性,必须通过构造器传进来:

<?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

Note:

注意,匿名类的名称是通过引擎赋予的,如下例所示。 由于实现的细节,不应该去依赖这个类名。

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

以上例程的输出类似于:

class@anonymous/in/oNi1A0x7f8636ad2021

User Contributed Notes

solobot 07-Nov-2017 10:33
eval() is workaround for generating multiple anonymous classes with static properties in loop
<?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;

        }
    }
?>
thus every class will have its own $tableName instead of common ancestor.
j.m \ jamesweb \ ca 04-Oct-2017 09:21
/* I like the idea of OneShot classes.
Thanks to that Anonymous bro\sist for precising  
new class( $a, $b )
ˉˉˉˉˉˉˉˉˉ

If you are looking for "Delayed OneShot Anonymous Classes" for any reason (like the reason: loading files in a readable manner while not using autoload), it would probably look something like this; */

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

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

// Usage;
var_dump(
    array(
        'Delayed',
        '( Self Destructive )',
        'Anonymous Class Creation',
        array(
            'Before ( $u )' => $u,
            'Running ( $u() )' => $u(),
            'After ( $u )' => $u,
        ),
        0,0,
        0,0,
        0,0,
        'Delayed',
        '( Overwriting && Self Destructive )',
        'Anonymous Class Creation',
        array(
            'Before ( $w )' => $w,
            'Running ( $w($u) )' => $w($u),
            'After ( $w )' => $w,
            'After ( $u )' => $u
        )
    )
);

// btw : oh shoot I failed a spam challenge
ytubeshareit at gmail dot com 06-May-2017 10:59
Anonymous classes are syntax sugar that may appear deceiving to some.
The 'anonymous' class is still parsed into the global scope, where it is auto assigned a name, and every time the class is needed, that global class definition is used.  Example to illustrate....

The anonymous class version...
<?php

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

//we can still access the 'anon' class directly in the global scope!
$another=get_class($test); //get the auto assigned name
echo $another::$str;    //outputs foo
?>

The above is functionally the same as doing this....
<?php
class I_named_this_one{
    public static
$str="foo";
}
function
return_not_anon(){
    return
'I_named_this_one';
}
$clzz=return_not_anon();//get class name
echo $clzz::$str;
?>
piotr at maslosoft dot com 26-Apr-2017 02:47
Please note that class name returned by `get_class` might contain null bytes, as is the case in my version of PHP (7.1.4).

Name will change when class starting line or it's body is changed.

Yes, name is implementation detail that should not be relied upon, but in some rare use cases it is required (annotating anonymous class).
primipilus13 at gmail dot com 27-Sep-2016 04:02
<?php

// using constructor and extends in anonymous class 

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;

// result: anonimous class
Anonymous 25-Mar-2016 03:52
Below three examples describe anonymous class with very simple and basic but quite understandable example

<?php
// First way - anonymous class assigned directly to variable
$ano_class_obj = new class{
    public
$prop1 = 'hello';
    public
$prop2 = 754;
    const
SETT = 'some config';

    public function
getValue()
    {
       
// do some operation
       
return 'some returned value';
    }

    public function
getValueWithArgu($str)
    {
       
// do some operation
       
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";

// Second way - anonymous class assigned to variable via defined function
$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()
        {
           
// do some operation
           
return 'some returned value';
        }

        public function
getValueWithArgu($str)
        {
           
// do some operation
           
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";

// Third way - passing argument to anonymous class via constructors
$arg = 1; // we got it by some operation
$config = [2, false]; // we got it by some operation
$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()
        {
           
// do some operation
           
return 'some returned value';
        }

        public function
getValueWithArgu($str)
        {
           
// do some operation
           
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";