2 minutes to read, 5.43K views since 2016.10.11 Читать на русском

Using getProperties to get the list of declared properties

Reflection gives to user possibility to get information about class, function(method of the class), class properties, modules and function parameters. Also Reflection gives possibility to create instances of classes (even with private constructors), invoking method of classes and passing values to properties and so on.

consider we have some class called Post:

class Post {

  public $id;

  /**
  * this field contains blog post's title
  */
  public $title; 

  /**
  * And this one its main content
  */  
  public $body; 

  public function save() {
    // this method saves model data to databse
  }

}

Imagine its a model class that is binded to database table and can be created or updated in it via method save():

$somePost->save();

I suggest save method would have similar to next implementation (shitty code):

public function save() {

    $sqlQuery = '';

    $setClause = '`title`="'.$this->title.'",'.
      '`body` = "'.$this->body.'"';

    if ($this->id > 0) {
      $sqlQuery = 'UPDATE `post` SET '.$setClause.' WHERE id = '.$this->id;
    } else {
      $sqlQuery = 'INSERT INTO `post` SET '.$setClause.', id = '.$this->id;
    }

    return $this->db->execute($sqlQuery);
}

What can we say about this code? Its hardcoded and can be used only in case of Post class. And what happen if we add some extra column, for example num_views which would save the number of post's views? - Yes, we should have our save method rewriten.

How can we solve this problem in general case? - By using a reflection api.

First usage of reflection api

Lets rewrite our save method:

public function save() {

    $class = new \ReflectionClass($this);
    $tableName = strtolower($class->getShortName);

    $propsToImplode = [];

    foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { // consider only public properties of the providen 
      $propertyName = $property->getName();
      $propsToImplode[] = '`'.$propertyName.'` = "'.$this->{$propertyName}.'"';
    }

    $setClause = implode(',',$propsToImplode); // glue all key value pairs together
    $sqlQuery = '';

    if ($this->id > 0) {
      $sqlQuery = 'UPDATE `'.tableName.'` SET '.$setClause.' WHERE id = '.$this->id;
    } else {
      $sqlQuery = 'INSERT INTO `'.tableName.'` SET '.$setClause.', id = '.$this->id;
    }

    try {
      return $this->db->execute($sqlQuery);
    } catch (\Exception $e) {
      // proper handling of exception
    }
}

In this case we dynamically get the table name (we use lowercased name of the class as a table name) and all the fields that should be updated or inserted.

Now we can use this method not only in Post class but in any class which should have such functionality and it would work as we expect.

I get the properties of the class which has only public visibility by providing a parameter to getProperties method of ReflectionClass instance:

$class->getProperties(\ReflectionProperty::IS_PUBLIC);

getProperties method of ReflectionClass returns array of ReflectionProperty objects. In this lesson we only used method getName which returns name of the property. e.g title for

public $title;

You can omit this this parameter to get all properties. Or you can get private or protected properties. You also can check for some visibility by methods of ReflectionProperty: isPublic,isPrivate or isProtected instead.

Here's what i think you need to know about getting properties on the fly in your php scripts.

Read next article Implementation of dependency injection in php in course Reflection in PHP