So I'm following a tutorial on Lynda.com regarding advanced PHP. They mention Late Static Bindings but I guess PHP 5.3 wasn't out when they made the tutorial so I'm trying to figure out how to do CRUD using a 'create' function in an extended class and pulling the attributes from the called class into the function. I feel like I'm missing something very simple, it insert a new row into my database, just no content. Heres the code, any suggestions would be useful.

PHP code in test.php file...

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

User class that extends DatabaseObject class...

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 with late static bindings...

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; 
  }

}
有帮助吗?

解决方案

The instance method can access its properies, the class method (static method) can not. So all the methods in DatabaseObject should be instance methods. For instance,

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;
    }
  } 
}

Late static bindins are appropriate for only $table_name and $db_fields.

其他提示

What you should do is set the static field $db_fields to the DatabaseObject if you want to continue to use this structure. Otherwise, change the type of the DatabaseObject to abstract, have an empty array for the database fieldset and set it to protected. As part of the __construct function for the implementation of DatabaseObject (User), have setFields->array(....) to set the mapped fields. Alternatively, you can allow direct property call like $user->foo, and use __get && __set magic methods and pre-define all allowable fields, rather than use a mapping array.

Here's the DatabaseObject class with printing the SQL:

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;
    }
}

Now if I run this as per your example I get the following output:

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 ('', '', '', '', '')

This is because the code doesn't know about the static object because it hasn't been set yet. You should probably consider using dynamic binding rather than static binding for better memory management and generally better performance. Static binding is useful in factories and registries where you need to maintain state whilst providing direct access to instances without instantiating the registry class (eg. $r = new Registry();$t = $r->get('foo'));

See the wiki article http://en.wikipedia.org/wiki/Name_binding

The other problem that you have is that get_called_class() will return the name of the class rather than the instance. By changing DatabaseObject to abstract and User implements DatabaseObject, you can modify the access of your functions and change the static methods to non-static, as you should.

Ideally when you're using methods they should be non-static so that with standard OOP programming practices, A extends B, $b is B, $b->foo() calls A::foo() where foo() is a non-static method of A.

Is there any reason you're using static binding in database access?

Edit --

Also want to say, you shouldn't mix static with non-static code, it just doesn't work, not in PHP and certainly not in any OOP code. If you need to use static binding you need to either

  • Keep a registry of items and pass some sort of reference to the database object you're working with
  • Keep a hash map of key => value sets
  • Pass the actual non-static object to the processing methods A::process($b) so that A::process() calls $b->$field.
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top