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');