内容管理系统-PHP后期静态绑定问题


content management system - PHP Late Static Binding Issue

所以我在Lynda.com上学习了一个关于高级PHP的教程。他们提到了Late Static Bindings,但我猜他们在制作教程时还没有推出PHP 5.3,所以我正试图弄清楚如何在扩展类中使用"create"函数进行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; 
  }
}

实例方法可以访问其属性,类方法(静态方法)不能。所以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。

如果要继续使用此结构,您应该将静态字段$db_fields设置为DatabaseObject。否则,将DatabaseObject的类型更改为abstract,为数据库字段设置一个空数组,并将其设置为protected。作为实现DatabaseObject(User)的__construct函数的一部分,使用setFields->array(....)设置映射字段。或者,您可以允许像$user->foo这样的直接属性调用,并使用__get&amp;__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->get('foo'));

查看wiki文章http://en.wikipedia.org/wiki/Name_binding

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

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

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

编辑--

还想说的是,你不应该把静态代码和非静态代码混合在一起,这是不起作用的,在PHP中也不起作用,当然在任何OOP代码中都不起作用。如果你需要使用静态绑定,你需要使用

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