PHP
PHP5.6

[ノート]『PHP Cookbook 』を読む(CH07 オブジェクト)(1)

More than 1 year has passed since last update.

7.1 Instantiating Objects

class user{
    function load_info($username){
        // load profile from database
    }

}
$user = new user;
$user->load_info($_GET['username']);

//These are two independent objects that happen to have identical information.
$adam = new user;
$adam->load_info('adam');
$dave = new user;
$dave->load_info('adam');

7.2 Defining Object Constructors

class user{
    public $username;
    function __construct($username,$password)
    {
        if($this->validate_user($username,$password)){
            $this->username=$username;
        }
    }
}

$user=new user('Grif','XXXXXXXX');

7.3 Defining Object Destructors

Objects are automatically destroyed when a script terminates.
To force the destruction of an object, use unset():

$car = new car; // buy new car // ...
unset($car); // car wreck

PHP supports object destructors.
Destructors are like constructors, except that they’re called when the object is deleted.

class car {
    function __destruct() {
        // head to car dealer
    }
}

You use a destructor to clean up after an object.
For instance, the Database destructor would disconnect from the database and free up the connection.

class Database{
    function __destruct()
    {
        db_close($this->handle);
    }
}

7.4 Implementing Access Control

Use the public, protected, and private keywords:

class Person {
    public $name; // accessible anywhere
    protected $age; // accessible within the class and child classes
    private $salary; // accessible only within this specific class
    public function __construct() { // ...
    }
    protected function set_age() { // ...
    }
}

public:Making a method or property public means anyone can call or edit it
protected:You can also label a method or property as protected, which restricts access to only the current class and any child classes that extend that class.
private:The final visibility is private, which is the most restrictive. Properties and methods that are private can only be accessed within that specific class.

When a program is designed with a high degree of encapsulation, the underlying data structures and database tables are not accessed directly.
Instead, you define a set of functions and route all your requests through these functions.

function getEmail($name) {
    $sqlite = new PDO("sqlite:/usr/local/users.db");
    $rows = $sqlite->query("SELECT email FROM users WHERE name LIKE '$name'");
    $row = $rows->fetch();
    $email = $row['email'];
    return $email;
}
$email = getEmail('Rasmus Lerdorf');

accessors:Objects allow you to wall off implementation internals from outside access. This pre‐ vents people from relying on code that may change and forces them to use your functions to reach the data. Functions of this type are known as accessors, because they allow access to otherwise protected information.

If you (or your team) are the only people using that class, choosing private
If you’re planning on distributing this code as a package, then biasing toward protected

7.5 Preventing Changes to Classes and Methods

Label the particular methods or class as final:

//To prevent subclassing of an entire class, don’t mark each method final. Instead, make a final class:
final class MySQL{

    //Class definition here

}
public class Connection{
//prevents someone from subclassing the class and creating a different connect() method.
    final public function connect($server, $username, $password)
   {
       //method definition here
   }
}

7.6 Defining Object Stringification

PHP provides objects with a way to control how they are converted to strings.

class Person{

    protected $name;
    protected $email;
    public function setName($name){
        $this->name=$name;
    }

    public function setEmail($email){
        $this->email=$email;
    }
    public function __toString()
    {

        return "$this->name<$this->email>";
    }
}

$rasmus=new Person;

$rasmus->setName('Rasums Lerdorf');
$rasmus->setEmail('rasums@php.net');

print $rasmus;

7.7Requiring Multiple Classes to Behave Similarly

You want multiple classes to use the same methods, but it doesn’t make sense for all the classes to inherit from a common parent class.

Define an interface and declare that your class will implement that interface:

interface NameInterface{
    public function getName();
    public function setName($name);
}

class Book implements NameInterface{
    private $name;

    public function getName()
    {
       return $this->name;
    }

    public function setName($name)
    {
        return $this->name=$name;
    }
}

When you want to include the code that implements the interface, define a trait and declare that your classes will use that trait:

trait NameTrait{
    private $name;

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        return $this->name=$name;
    }
}

class Child{
    use NameTrait;
}

interface:The mechanism for forcing classes to support the same set of methods is called an interface.
Defining an interface is similar to defining a class:

interface NameInterface02{
    public function getName();
    public function setName($name);
}

When a class supports all the methods in the interface, it’s said to implement the interface.

class Book02 implements NameInterface02 { 
    private $name;
    public function getName() {
        return $this->name;
    }
    public function setName($name) {
        return $this->name = $name;
    }
}

When you use interfaces, it’s important to declare your classes before you instantiate objects.
To check if a class implements a specific interface, use class_implements(), as shown:

$interfaces=class_implements('Book02');
if(isset($interfaces['NameInterface02'])){
    print "Book02 implements NameInterface\n";
}

use interfaces and traits together. This is actually a best-practice design:

class Book03 implements NameInterface {
    use NameTrait;
}

Interfaces allow you to establish clear contracts with explicit promises about how your objects behave. Traits allow you to reuse code across objects that don’t have an “is a” inheritance relationship; they are just a programmatic way to avoid copy and pasting code in multiple places.Interfaces combined with traits give you the best of both.

7.8 Creating Abstract Base Classes
Do this by placing the abstract keyword before the class definition,and You must also define at least one abstract method in your class

abstract class Database{
 abstract public function connect($server,$username,$password,$database);
 abstract public function query($sql);
 abstract public function fetch();
 abstract public function close();
}

Abstract classes are best used when you have a series of objects that are related using the is a relationship. Therefore, it makes logical sense to have them descend from a common parent.
abstract methods are implemented in a child class that extends the abstract parent

class MySQL extends Database{
    protected $dbh;
    protected $query;

    public function connect($server,$username,$password,$database)
    {
        $this->dbh=mysqli_connect($server,$username,$password,$database)
;    }

    public function query($sql)
    {
       $this->query=mysqli_query($this->dbh,$sql);
    }

    public function fetch()
    {
        return mysqli_fetch_row($this->dbh,$this->query);
    }

    public function close()
    {
        mysqli_close($this->dbh);
    }
}

If a subclass fails to implement all the abstract methods in the parent class, then it itself is abstract and another class must come along and further subclass the child.

There are two requirements for abstract methods:
• Abstract methods cannot be defined private, because they need to be inherited.
• Abstract methods cannot be defined final, because they need to be overridden.

you can implement multiple interfaces, but extend only one abstract class. Additionally, in an interface you can only define method prototypes—you cannot implement them. An abstract class, in comparison, needs only one abstract method to be abstract, and can have many nonabstract methods and even properties.
You should also use abstract classes when the “is a” rule applies.

7.9 Assigning Object References

Use = to assign one object to another by reference:

class User01{
    private $name;
    public function setName($name){
        return $this->name=$name;
    }
    public function load_info(){
        //this is code
        print $this->name;
    }

}

$adam=new User01();
$adam->setName('adam');
$adam->load_info();
$dave=$adam;
var_dump($dave);

When you do an object assignment using =, you don’t create a new copy of an object, but a reference to the first. So, modifying one alters the other.

7.10 Cloning Objects

$rasmus = clone $zeev;

This cloning process copies every property in the first object to the second. This includes properties holding objects, so the cloned object may end up sharing object references with the original.

class Address{
    protected $city;
    protected $country;
    public function setCity($city) { $this->city = $city; }
    public function getCity() { return $this->city; }
    public function setCountry($country) { $this->country = $country; }
    public function getCountry() { return $this-> country;}
}

class Person{
    protected $name;
    protected $address;
    public function __construct(){
        $this->address=new Address();
    }
    public function setName($name){
        $this->name=$name;
    }
    public function getName(){
        return $this->name;
    }
    public function __call($method, $arguments)
    {
        if(method_exists($this->address,$method)){
            //$method is like getCountry in the obj address
            return call_user_func_array(array($this->address,$method),$arguments);
        };
    }

//Calling setName() worked correctly because the $name property is a string, so it’s copied by value. However, because $address is an object, it’s copied by reference, so getCity() doesn’t produce the correct results,

//This type of object cloning is known as a shallow clone or a shallow copy. In contrast, a deep clone occurs when all objects involved are cloned.
//Control how PHP clones an object by implementing a __clone() method in your class.
    public function __clone()
    {
       $this->address=clone $this->address;
    }

}
$rasmus=new Person();
$rasmus->setName('Rasmus Lerdorf');
$rasmus->setCity('Sunnyvale');

$zeev=clone $rasmus;
$zeev->setName('Zeev Suraski');
$zeev->setCity('Tel Aviv');

print $rasmus->getName() . ' lives in ' . $rasmus->getCity() . '.';
print "\n";
print $zeev->getName() . ' lives in ' . $zeev->getCity() . '.';

7.11 Overriding Property Accesses

Use the magic methods __get() and __set() to intercept property requests.this lets you write generalized code to handle property access in your class.
Property overloading allows you to seamlessly obscure from the user the actual location of your object’s properties and the data structure you use to store them.

class Person{
    private $__data=array();
    public function __get($property){
        if(isset($this->__data[$property])){
            return $this->__data[$property];
        }else{
            return false;
        }

    }
    public function __set($property, $value)
    {
       $this->__data[$property]=$value;
    }
}

$person1=new Person();
$person1->age=23;
$person1->name="alice";
print $person1->age;
var_dump($person1);

7.12 Calling Methods on an Object Returned by Another Method

$orange=$fruit->get('citrus')->peel();

7.13 Aggregating Objects

class Address{
    protected  $city;


    public function getCity()
    {
        return $this->city;
    }


    public function setCity($city)
    {
        $this->city = $city;
    }

}

class Person{
    protected $name;
    protected $address;

    public function __construct()
    {
        $this->address=new Address();
    }


    public function getName()
    {
        return $this->name;
    }


    public function setName($name)
    {
        $this->name = $name;
    }
    public function __call($method, $arguments)
    {
        if(method_exists($this->address,$method)){
            return call_user_func_array(
                array($this->address,$method),$arguments
            );
        }

    }
}

$rasmus = new Person; $rasmus->setName('Rasmus Lerdorf');
$rasmus->setCity('Sunnyvale');
print $rasmus->getName() . ' lives in ' . $rasmus->getCity() . '.';

7.14 Accessing Overridden Methods

Prefix parent to the method name:

class shape{
    function draw(){
        //write to screen
    }
}

class circle extends shape{
    function draw ($origin,$radius){
        // validate data
        if($radius>0){
            parent::draw();
            return true;
        }
        return false;
    }
}

Only code inside the class can use parent::. Calling parent::draw() from outside the class gets you a parse error.

This also applies to object constructors

class circle02 extends shape {

    function __construct($x,$y,$r)
    {
        // call shape's constructor first
        parent::__construct();
        // now do circle-specific stuff
    }
}

7.15 Creating Methods Dynamically

Use the __call() and __callStatic() magic methods to intercept method invocations and route them accordingly.
This technique is best used when you’re providing an object relational map (ORM) or creating a proxy class. For instance, you want to expose findBy() methods that translate to database queries or RESTful APIs.

class Users{
    static function find($args){
        //here's where the real logic lives
        // for example a database query:
        // SELECT user FROM users WHERE $args['field'] = $args['value']
        //TODO:fix the result;
    }
    static function __callStatic($method, $args)
    {
       if(preg_match('/^findBy(.+)$/', $method, $matches)){
           return static::find(array('field'=>$matches[1],'value'=>$args[0]));
       }
    }


}
$user=Users::findById(123);
$user=Users::findByEmail('rasmus@php.net');