PHP 后期静态绑定问题

Posted

技术标签:

【中文标题】PHP 后期静态绑定问题【英文标题】:PHP Late Static Binding Issue 【发布时间】:2014-04-17 02:43:15 【问题描述】:

所以我正在关注 Lynda.com 上有关高级 php 的教程。他们提到了后期静态绑定,但我猜他们制作教程时 PHP 5.3 还没有推出,所以我试图弄清楚如何在扩展类中使用“创建”函数进行 CRUD,并将属性从被调用的类中提取到功能。我觉得我错过了一些非常简单的东西,它在我的数据库中插入了一个新行,只是没有内容。这是代码,任何建议都会很有用。

test.php 文件中的 PHP 代码...

<?php
$user = new User();
$user->username = "johnsmith";
$user->password = "555";
$user->first_name = "john";
$user->last_name = "smith";
$user->create();
?>

扩展 DatabaseObject 类的用户类...

class User extends DatabaseObject 


    protected static $table_name="users";
    protected static $db_fields = array('id', 'username', 'password', 'first_name', 'last_name');
    public $id;
    public $username;
    public $password;
    public $first_name;
    public $last_name;
 

具有后期静态绑定的DatabaseObject 类...

class DatabaseObject 

protected static function attributes() 
 $attributes = array();
 foreach(static::$db_fields as $field) 

    if(property_exists(get_called_class(), $field)) 
    $attributes[$field] = get_called_class()->$field;
    
      
      return $attributes;
          

protected static function sanitized_attributes() 
 global $database;
     $clean_attributes = array();
 foreach(self::attributes() as $key => $value) 
    $clean_attributes[$key] = $database->escape_value($value);
      
      return $clean_attributes;
      


public static function create()  
 global $database;

  $attributes = self::sanitized_attributes();
  $sql = "INSERT INTO ".static::$table_name." (";
  $sql .= join(",", array_keys($attributes));
  $sql .=") VALUES ('";
  $sql .= join("', '", array_values($attributes));
  $sql .= "')";

  if($database->query($sql))  
   static::$id = $database->insert_id();// get last id saved in database and puts it in the id arguement
   return true;
   else 
    return false; 
  


【问题讨论】:

请参阅:php.net/manual/en/function.get-called-class.php 获取 get_call_class() 【参考方案1】:

实例方法可以访问其属性,类方法(静态方法)不能。 所以 DatabaseObject 中的所有方法都应该是实例方法。 例如,

class DatabaseObject 
  protected function attributes() 
    $attributes = array();
    foreach(static::$db_fields as $field) 
      if(property_exists(get_called_class(), $field)) 
        $attributes[$field] = $this->$field;
      
    
    return $attributes;
  
  protected function sanitized_attributes() 
    global $database;
    $clean_attributes = array();
    foreach(self::attributes() as $key => $value) 
      $clean_attributes[$key] = $database->escape_value($value);
    
    return $clean_attributes;
  
  public function create() 
    global $database;
    $attributes = self::sanitized_attributes();
    $sql = "INSERT INTO ".static::$table_name." (";
    $sql .= join(",", array_keys($attributes));
    $sql .=") VALUES ('";
    $sql .= join("', '", array_values($attributes));
    $sql .= "')";
    if($database->query($sql)) 
      $this->id = $database->insert_id();// get last id saved in database and puts it in the id arguement
      return true;
     else 
      return false;
    
   

后期静态绑定仅适用于 $table_name 和 $db_fields。

【讨论】:

【参考方案2】:

如果您想继续使用此结构,您应该将静态字段 $db_fields 设置为 DatabaseObject。否则,将 DatabaseObject 的类型更改为抽象,为数据库字段集设置一个空数组并将其设置为受保护。作为用于实现 DatabaseObject (User) 的 __construct 函数的一部分,有setFields-&gt;array(....) 来设置映射字段。或者,您可以允许像$user-&gt;foo 这样的直接属性调用,并使用__get && __set 魔术方法并预定义所有允许的字段,而不是使用映射数组。

这是打印 SQL 的 DatabaseObject 类:

class DatabaseObject 
    protected static function attributes() 
        $attributes = array();
            foreach(static::$db_fields as $field) 
                if(property_exists(get_called_class(), $field)) 
                    $attributes[$field] = get_called_class()->$field;
                
            
        return $attributes;
    

    protected static function sanitized_attributes() 
        $clean_attributes = array();
        foreach(self::attributes() as $key => $value) 
            $clean_attributes[$key] = $value;
        
        return $clean_attributes;
    

    public static function create()  
        $attributes = self::sanitized_attributes();
        $sql = "INSERT INTO ".static::$table_name." (";
        $sql .= join(",", array_keys($attributes));
        $sql .=") VALUES ('";
        $sql .= join("', '", array_values($attributes));
        $sql .= "')";

        print $sql;
    

现在,如果我按照您的示例运行它,我会得到以下输出:

php > $user->create();
PHP Notice:  Trying to get property of non-object in php shell code on line 6
PHP Notice:  Trying to get property of non-object in php shell code on line 6
PHP Notice:  Trying to get property of non-object in php shell code on line 6
PHP Notice:  Trying to get property of non-object in php shell code on line 6
PHP Notice:  Trying to get property of non-object in php shell code on line 6
INSERT INTO users (id,username,password,first_name,last_name) VALUES ('', '', '', '', '')

这是因为代码不知道静态对象,因为它尚未设置。您可能应该考虑使用动态绑定而不是静态绑定以获得更好的内存管理和更好的性能。静态绑定在需要维护状态同时提供对实例的直接访问而无需实例化注册表类的工厂和注册表中很有用(例如,$r = new Registry();$t = $r-&gt;get('foo'));

参见维基文章http://en.wikipedia.org/wiki/Name_binding

您遇到的另一个问题是get_called_class() 将返回类的名称而不是实例的名称。通过将 DatabaseObject 更改为 abstractUser 实现 DatabaseObject,您可以修改函数的访问权限并将静态方法更改为非静态方法。

理想情况下,当您使用方法时,它们应该是非静态的,这样在标准 OOP 编程实践中,A 扩展 B,$b 是 B,$b->foo() 调用 A::foo() where foo( ) 是 A 的非静态方法。

您在数据库访问中使用静态绑定有什么原因吗?

编辑 --

还想说,您不应该将静态代码与非静态代码混用,它只是行不通,在 PHP 中不行,当然也不在任何 OOP 代码中。如果您需要使用静态绑定,则需要

保留项目注册表并将某种引用传递给您正在使用的数据库对象 保留键 => 值集的哈希映射 将实际的非静态对象传递给处理方法A::process($b),以便A::process() 调用$b-&gt;$field

【讨论】:

我正在关注 Lynda.com 教程。基本上,这只是他们正在设置的 CRUD 的创建部分。他们拥有它,因此每个对象(用户用于登录,创建新的管理员用户/密码,还有另一张照片,用于将图像上传到目录)包括这些功能中的每一个,因为当他们制作教程 PHP 5.3还没出来。基本上,我想创建一个 Create、Read、Update 和 Delete 方法,只需扩展 CRUD 方法所在的对象,就可以轻松地处理各种对象。 那么我所要做的就是在新对象中创建分配并像上面一样分配它们变量,然后调用 create() 或 read() 或 update() 方法与数据库。我仍在努力学习 PHP 和 OOP,所以请耐心等待。感谢您的回复,非常有用! 最好的例子是 Zend/Symfony 所做的,但如果你还在学习 CRUD,那就有点高级了。理想情况下,使用 CRUD,您有一个数据库层,其中包含为您提供基本 CRUD 命令的数据库对象$obj-&gt;create()$obj-&gt;read()$obj-&gt;update()$obj-&gt;delete()。您可以让用户扩展您的 CRUD 对象 ($a = new User(); $a-&gt;update()),或者将您的用户对象注入到 CRUD 服务中(例如,$a = new Entity($user); $a-&gt;create();)。两种实现都有效,只取决于您的需要。 这是服务示例,它在 c# 中,但您应该能够关注:msdn.microsoft.com/en-us/library/gg328416.aspx 知道了,是的,上面的 #c 示例对我来说有点太高级了,但我很感激您的反馈!

以上是关于PHP 后期静态绑定问题的主要内容,如果未能解决你的问题,请参考以下文章

PHP面向对象-后期静态绑定

后期静态绑定在PHP中的使用

后期静态绑定在PHP中的使用

单例中的 PHP 后期静态绑定

使用后期静态绑定引用 self 的 PHP 语法

PHP中的后期静态绑定(Late Static Bindings )