是否有相当于实例化类的 __get 魔术方法?

发布于 2024-09-08 17:11:30 字数 502 浏览 1 评论 0原文

寻找是否有任何方法可以实现 __get() 魔术方法的功能,但是当尝试实例化一个新类时。

我的目标是让

$foo = new Cat();

最终创建一个通用对象,即

new Mammal('Cat');

这样 >$foo 将是类 Mammal 的实例,调用的类名 ('Cat') 作为参数传递给 Mammal< /code> 的构造函数。


注意: 对于那些熟悉 Lithium 或 CakePHP 的人来说,我想到的最终目标是避免为每个不同的数据库表定义一大堆类。如果我这样做了,大多数都只是空的,基本的 CRUD 操作就足够了。另外,所有这些包含和类定义对于开销来说并不是很好。我的想法是使用单个“模型”类来处理大多数通用函数,如果我需要特定数据库表的更高级功能,我总是可以创建子类。

Looking to see if there is any way to implement the kind of functionality of the __get() magic method, but when trying to instantiate a new class.

My goal is to have

$foo = new Cat();

Ultimately result in the creation of a generic object, i.e.

new Mammal('Cat');

Such that $foo would be an instance of the class Mammal, with the called class name ('Cat') passed as an argument to Mammal's constructor.


Note:
The end game I have in mind, for those familiar with Lithium or CakePHP, is to avoid having to define a whole bunch of classes for each different database table. If I did, most would simply be empty, the basic CRUD operations being all that is necessary. Plus, all those includes and class definitions can't be great for overhead. My idea would be to use a single "Model" class to handle most of the generic functions, and I could always create subclasses if I needed more advanced functionality for a specific database table.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

紫瑟鸿黎 2024-09-15 17:11:31

我认为没有任何直接的非 hacky 方法可以通过类实例化机制实现您想要的目标。

我建议使用工厂:

$tableFactory->createTable('Cat');

然后它会计算出需要在幕后做什么。这还有一个好处,如果您决定 Cat 需要它自己的类,您可以简单地修改工厂,并且可能不需要修改使用它的代码。

I don't think there is any straightforward non-hacky way to achieve what you want with the class instantiation mechanism.

I would recommend using a factory:

$tableFactory->createTable('Cat');

Which would then work out what needs to be done behind the scenes. This also has the benefit of if you ever decide Cat requires its own class, you can simply modify the factory and you may not need to modify the code that is using it.

葬シ愛 2024-09-15 17:11:31

好吧,这有点 hacky,但你可以滥用 类自动加载器...

所以,劫持加载器来检查类“Cat”,如果它不存在,则将其评估为存在...

if (!$foundClass) {

    $code = 'Class '.$class.' extends Mammal { 
        public function __construct() { parent::__construct("'.$class.'");}
    }';
    eval($code);
}

我说这是 hacky...但您始终可以将其列入白名单您想要对其执行此操作的类...另外,它的好处是,如果您想修改该类,只需创建它的固定实例即可。

但话又说回来,我个人会寻找另一种解决方案。对我来说,new Cat() 根本不可读。这是因为两个原因,首先没有上下文线索说明它是什么,其次我找不到 Cat 类...我要做的与 Jani 建议的类似: $table = DatabaseTable::getInstance ('cat');...我发现它更具可读性(即使它有更多字符)...

Well, it's a bit hacky, but you could abuse the class autoloader...

So, hijack the loader to check for a class "Cat", if it doesn't exist, then eval it into existance...

if (!$foundClass) {

    $code = 'Class '.$class.' extends Mammal { 
        public function __construct() { parent::__construct("'.$class.'");}
    }';
    eval($code);
}

I said it was hacky... But you could always white-list the classes you want to do this for... Plus, it has the benefit that if you wanted to modify the class, just create a firm instance of it.

But then again, I would personally find another solution. To me, new Cat() is not readable at all. That's because of two reasons, first there's no contextual clues as to what it is and second I can't find the class Cat... What I would do is similar to what Jani suggested: $table = DatabaseTable::getInstance('cat');... I find that MUCH more readable (even though it is more characters)...

故事灯 2024-09-15 17:11:31

我在 Cake 中记得的方式是,在控制器类之上定义模型,然后简单地使用 $this->Modelname。实现起来应该很简单:

public function __get($prop)
{
    if(in_array($prop, $this->uses)) {
        return new $prop;
    }
}

每次调用不存在的属性时,您的类都会检查该属性名称是否存在于数组 $uses 中,如果存在,则假设 $prop 是类名并实例化它。您需要将实例存储在某个地方,以避免每次获取它时都重新实例化。

这比到处写 new Klass 更干净一些,因为这使得很难将 Klass 换成其他东西。然后将其硬连接到控制器中。这是您想要避免的依赖。话虽如此,您可能想看看 Symfony 依赖注入框架

The way I remember it from Cake is that you define the model on top of the controller class and then simply use $this->Modelname. This should be as simple to implement as:

public function __get($prop)
{
    if(in_array($prop, $this->uses)) {
        return new $prop;
    }
}

Each time you call a non-existing property, your class would check if the property name exists in an array $uses and if so, assumes $prop is classname and instaniates it. You will want to store the instance somewhere to avoid reinstantiating each time you fetch it.

This is somewhat cleaner than writing new Klass all over the place, because that makes it hard to exchange Klass for something else. You hardwire it into the controller then. It's a dependency you want to avoid. With that said, you might want to have a look at the Symfony Dependency Injection framework.

撞了怀 2024-09-15 17:11:31

我知道这是一个老问题,但对很多人来说仍然有意义。

我克服这种情况的方法是拥有一个包含所有基本表交互的基本建模器:

<?php
    class base {

        /**
         * @name        $table_values
         * @description This holds all data about the table including the field names and data for the record loaded.
         * @example     {
         *                  "primary_keys"  :   [],
         *                  "table_data"    :   {
         *                                          "name"  :   "value",
         *                                          "name"  :   "value"
         *                                      }
         *              }
         */
        private $table_values = array();

        /**
         * @name        __get
         * @param       $name   The name of the property to return.
         * @description The PHP magic getter.
         */
        public function __get($name) {
            $keys = array_keys($this->table_values['table_data']);
            if (in_array($name, $keys)) {
                return $this->table_values['table_data'][$name];
            }
        }

        /**
         * @name        __set
         * @param       $name   The name of the property to set.
         * @param       $value  The value to assign to the property.
         * @description The PHP magic setter.
         */
        public function __set($name, $value) {
            $this->table_values['table_data'][$name] = $value
        }

        /**
         * @name        table_name
         * @description Must override in child class. Should return the name of the table to use.
         */
        public function table_name() {}

        /**
         * @name        load
         * @param       $ids    int|array   Can be a single primary key or an assoc array of primary keys depending on the table with the keys for the array being the field names.
         * @description This method handles loading a single record from the table and storing it as a PHP object.
         */
        public function load($ids) {
            // Use the primary key(s) to find and load the record into $table_values from the database.
        }

        /**
         * @name        save
         * @description Saves the record in the database
        public function save() {
            // Use the primary key(s) to find and either insert or update the database table record.
        }
    }
?>

将基类用作:

<?php
    class person extends base {
        public function table_name() {
            return "person";
        }
    }

    class car extends base {
        public function table_name() {
            return "car";
        }
    }
?>

等。

使用自动加载器自动加载类并处理表不存在类的情况

<?php
    spl_autoload_register(function($class_name) {
        $filename = "/path/to/class/{$class_name}.php";
        if (class_exists($class_name)) {
            // Do nothing.
        } elesif (file_exists($filename)) {
            require_once($filename);
        } else {
            // Check if the class name exists in the database as a table.
            if ($database->table_exists($class_name)) {
                $class_def = "
class {$class_name} extends base {
    public function table_name() {
        return \"{$class_name}\";
    }
}";
                eval($class_def);
            }
        }
    });
?>

:基类可以有更多的功能。我还具有在子项中重写的功能,以便在加载和保存之前和之后执行操作,以及检查具有相同数据的重复记录。

I know this is an old question now but still relevant today for a lot of people.

My way of overcoming this exact scenario is to have a base modeler which holds all basic table interaction:

<?php
    class base {

        /**
         * @name        $table_values
         * @description This holds all data about the table including the field names and data for the record loaded.
         * @example     {
         *                  "primary_keys"  :   [],
         *                  "table_data"    :   {
         *                                          "name"  :   "value",
         *                                          "name"  :   "value"
         *                                      }
         *              }
         */
        private $table_values = array();

        /**
         * @name        __get
         * @param       $name   The name of the property to return.
         * @description The PHP magic getter.
         */
        public function __get($name) {
            $keys = array_keys($this->table_values['table_data']);
            if (in_array($name, $keys)) {
                return $this->table_values['table_data'][$name];
            }
        }

        /**
         * @name        __set
         * @param       $name   The name of the property to set.
         * @param       $value  The value to assign to the property.
         * @description The PHP magic setter.
         */
        public function __set($name, $value) {
            $this->table_values['table_data'][$name] = $value
        }

        /**
         * @name        table_name
         * @description Must override in child class. Should return the name of the table to use.
         */
        public function table_name() {}

        /**
         * @name        load
         * @param       $ids    int|array   Can be a single primary key or an assoc array of primary keys depending on the table with the keys for the array being the field names.
         * @description This method handles loading a single record from the table and storing it as a PHP object.
         */
        public function load($ids) {
            // Use the primary key(s) to find and load the record into $table_values from the database.
        }

        /**
         * @name        save
         * @description Saves the record in the database
        public function save() {
            // Use the primary key(s) to find and either insert or update the database table record.
        }
    }
?>

Use the base class as:

<?php
    class person extends base {
        public function table_name() {
            return "person";
        }
    }

    class car extends base {
        public function table_name() {
            return "car";
        }
    }
?>

etc.

Use and autoloader to automatically load classes and to handle when a class doesn't exist for a table:

<?php
    spl_autoload_register(function($class_name) {
        $filename = "/path/to/class/{$class_name}.php";
        if (class_exists($class_name)) {
            // Do nothing.
        } elesif (file_exists($filename)) {
            require_once($filename);
        } else {
            // Check if the class name exists in the database as a table.
            if ($database->table_exists($class_name)) {
                $class_def = "
class {$class_name} extends base {
    public function table_name() {
        return \"{$class_name}\";
    }
}";
                eval($class_def);
            }
        }
    });
?>

The base class can have a lot more functionality. I also have functions to override in children to do things before and after load and save, and to check for duplicate records with the same data for instance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文