PHP Classes – Oblects notes

Object oriented programming logic is very usefull if you want your code to reach your imagination limits. OOP helps your code to extend easier and at the same time helps your scripts to be cleaner, reusable, and saperated according to the logic you choose. Much stuff here is taken from the official php manual paraphrased and summarized by me in an efford to create a quick guide to PHP classes and objects. Most of the examples are mine and I tried to make them playful and educational.

 

New

An object will always be created unless the object has a constructor defined that throws an exception on error.
Classes should be defined before instantiation.
When assigning an already created instance of a class to a new variable, the new variable will access the same instance as the object that was assigned. This behaviour is the same when passing instances to a function. A copy of an already created object can be made by cloning it.
If the class is in a namespace, its fully qualified name must be used when doing this.

<?php

class Person{
      public $name = 'John Doe';
      public $age = '32';
      public $nationality = 'Greece';
      public function say_hello(){
          echo $this->name.' says hello.<br>';
      }
      public function introduce(){
          echo 'My name is '.$this->name.' I\'m '.$this->age.' years old, and I\'m from '.$this->nationality.'.<br>';
     }
}

$person = new Person();
$person->say_hello();
$person->introduce();
$person->age += 1;
$person->introduce();

?>

<?php

class Vehicle{

    public $wheels = 4;
    public $radio = true;
    public $top_speed = 100;
    public $speed = 10;	
    protected $accelarator = 5;

    protected function accelarate(){
        $this->speed += $this->accelarator;
        echo $this->speed.' - ';
    }
    public function race(){
        if( $this->speed > $this->top_speed){
            $this->flat_tire();
            return false;
        }
        while( $this->speed <= $this->top_speed ){
           $this->accelarate();
       }
       echo 'Top speed reached! <br>';
    }
    public function flat_tire(){
       $this->wheels -= 1;
       $this->speed = 0;
       echo 'Active wheels: '.$this->wheels.'. Time to change tire.<br>';
    }
    public function change_tire(){
      $this->wheels += 1;
      echo 'Active wheels: '.$this->wheels.'. Time to race again.<br>';		
    }

}

$car = new Vehicle();
$car->race();
$car->race();
$car->change_tire();
$car->race();

?>

Visibility

public (props+methods can be accessed everywhere. Default if no visibility used)
protected (props+methods can be accessed by the class and by inheriting classes)
private (props+methods can be accessed by the class that defines the member)

<?php

class Alfa{
      public $public_a = 'A';
      protected $protected_a = 'A';
      private $private_a = 'A';
}
class Vita extends Alfa{
      public $public_b = 'B';
      protected $protected_b = 'B';
      private $private_b = 'B';
      function write(){
           echo $this->public_b.''.$this->protected_b.''.$this->private_b.''.
                $this->public_a.''.$this->protected_a.''.$this->private_a.'';
      }
}

$b = new Vita();

echo $b->public_b.'<br>';
echo $b->public_a.'<br>';
$b->write();     // BBBAA
$b->protected_b; //Error
$b->private_b;   //Error

?>

Static

Properties or methods accessible without needing on istantiation of the class.
$this is not available in static methods
static properties cannot be accessed with ->

<?php

class Dog{
     public static $type = 'pet';
     public static function bark(){
            echo 'waf-waf';
     } 
} 

Dog::bark();
$animal = 'Dog';
$animal::bark();
echo Dog::$type;
echo $animal::$type;

?>

Extends

The subclass inherits all the public and protected methods.
The subclass can overidde methods of the parent class.
Not possible to extend multiple classes. A class can inherit only from one base class.
It is possible to access the overridden methods or static properties by referencing them with parent::.
When overriding methods, the parameter signature should remain the same. This does not apply to the costructor.

<?php

class Office{
      public $employees = 3;
      public $computers = 3;
      protected $number = 1;
      private $budget = '400€';
      public function statistics(){
             echo 'Office number: '.$this->number.' with '.$this->employees.
                  ' employees and '.$this->computers.' pc\'s.Monthly budget:'.
                  $this->budget.'<br>';
     }
}

class Company extends Office{
     public $employees = 9;
     public $computers = 4;
     public $number = 2;
     private $budget = '600€'; //does not overidde the parent
}

$company = new Company();
$company->statistics();

?>

Scope Resolution Operator (::)

Allows access to static, constant and overridden properties or methods of a class
3 special keywords self, parent and static are used to access properties or methods from inside the class definition.

<?php

class School{
      const BOARD = 10;
      public static $students = 240;
      public static function report(){ 
             echo 'School has '.self::BOARD.' boards, and '.self::$students.' students.<br>';
      }
}

class Class_room extends School{
      const BOARD = 1;
      public static $students = 30;
      public static function report(){
             parent::report();
             echo 'Class room has '.self::$students.' and '.self::BOARD.' boards.<br>';
      }
     public static function stats(){
            $percentage = (self::$students/parent::$students)*100;
            echo 'This class has '.$percentage.'% of the school students.<br>';
            parent::$students-=self::$students;
            echo parent::$students.' more children to examine.';
     }
} 

$c = 'Class_room';
$c::report();
$c::stats();

?>

Constants

Can define constants per class basis.
Default visibility is public.
The value must be a constant expression.
Interfaces can have constants.

<?php

class Car1{
      const MAX_SPEED = '180km';
}
class Car2{
      const MAX_SPEED = '220km';	
} 

$car = new Car1();
echo $car::MAX_SPEED;
$car = new Car2();
echo $car::MAX_SPEED;

?>

Final

Prevents child classes from overriding a method.
A final class cannot be extended.

<?php

final class Total{
      //final is not mandatory here
      final public statictics(){
      ...
      }
}
//Fatal error
class SuperTotal extends Total{}

?>

Class

You can get a string containing the fully qualified name of the ClassName class by using ClassName::class.Useful with namespaced classes.

<?php

class ClassName {
      public function expose(){
             echo 'My name is '.ClassName::class;
      }
}

$cl = new ClassName;
$cl->expose();    

?>

Abstraction

Abstract classes may not be instantiated.
Any class that contains at least one abstract method must also be abstract.
When inheriting from abstract, all methods marked abstract in the parent must be defined in the child and additionally these methods must be defined with the same or less restricted visibility and the signatures of the methods must match (the type hints and the number of arguments).

<?php

abstract class Stage{
		abstract protected function setTimer();
		abstract protected function startTimer();
		public function newStage($number,$seconds){
			echo 'Stage '.$number.' begins.<br>';
			echo $this->setTimer($seconds);
			echo $this->startTimer();
		}

}

class Stage_1 extends Stage{
		public $seconds=30;
		public function setTimer(){
			return 'Timer started at '.$this->seconds.' seconds.<br>';
		}
		public function startTimer(){
			$output = '';
			while( $this->seconds > 0 ){
				$this->seconds -= 1;
				$output.= $this->seconds.' left<br>';
			}
			$output.= 'No more time left. Game over.';
			return $output;
		}
}

$stage = new Stage_1();
$stage->newStage(1,30);

?>

Cloning

By using the clone keyword(which calls the object __clone method if possible)
An object’s clone() method cannot be called directly.
Shallow copy. Props that are referances will remain referances.
Once cloning is complete, if a __clone() method is defined, then the newly created object’s __clone() method will be called, to allow any necessary properties that need to be changed.

<?php

class SubObject
{
	static $instances = 0;
	public $instance;

	public function __construct() {
		$this->instance = ++self::$instances;
	}

	public function __clone() {
		$this->instance = ++self::$instances;
	}
}

class MyCloneable
{
	public $object1;
	public $object2;

	function __clone(){
	// Force a copy of this->object, otherwise
	// it will point to same object.
	$this->object1 = clone $this->object1;
	}
}

$obj = new MyCloneable();
$obj->object1 = new SubObject();
$obj->object2 = new SubObject();

$obj2 = clone $obj;

print("Original Object:\n");
print_r($obj);
echo '<hr>';
print("Cloned Object:\n");
print_r($obj2);

?>

Constructors and destructors

Contsructor Destructor
void __construct([ mixed $args = "" [, $... ]] ) void __destruct(void)
Parent constructor destructor will NOT be called implicitly. In order to run them we have to explicitly call them parent::__construct(), parent::__destruct
A child class may inherit constructors, destructors if it does not implement one itself.
PHP will generate E_STRICT level error when contruct is overidden with diffenrent params than the parent construct method has. The destructor method will be called as soon as there are no other referances to a particular object, or in any order during the shutdown sequence.
Methods with the same name as the last element of a namespaced class name will no longer be treat as a constructor. this change doesn’t effect namespaced classes. The destructor will be called even if script execution is stopped using exit(). Calling exit() in a destructor will prevent the remaining shutdown routines from executing.
Attempt to throw an exception from a destructor causes a fatal error.

<?php

class Start_History{
	public static $counter = -230000000;
	function __construct() {
		$this->now = date('h:m:s');
		echo 'The construction started at '.$this->now.'<br>';
		echo 'Dinosaurs lived between 230 and 65 million years ago, in a time known as the Mesozoic Era.<br>';
	} 
}

class History_Till_Humans extends Start_History{
	public function narration(){
		while(parent::$counter<65000000){
			parent::$counter+=10; 
		} 
	}
		function __destruct() {
			$this->now = date('h:m:s');
			echo 'Dinosaurs vanished after a comet impact before sixty-five million years.<br>';
			echo 'Destroyed at '.$this->now .'<br>';		
		} 
}

$history = new History_Till_Humans();
$history->narration();


?>

Object Interfaces

Create code which specifies which methods a class must implement.

Classes may implement more than one interface.
Can be extended.
Can define constants, and they cannot be overidden.
All methods must be public.
The class implementing the interface must use the exact same method signatures as are defined in the interface. Not doing so will result in a fatal error.

<?php

interface Movement{
		const MAX_X = 100;
		const MAX_Y = 80;
		public function move_x($dir,$dice);
		public function move_y($dir,$dice);
		public function give_position();
		public function give_status();
		public function check_for_win();
}

class Pawn implements Movement{
		public $x = 50;
		public $y = 50;
		public $win_x = 0;
		public $win_y = 0;
		public $status = true;
		public $player; 

		public function __construct($player) {
			$this->player = $player;
		}

		public function check_for_win(){
			if( $this->x == $this->win_x && $this->y == $this->win_y ){
				$this->status = false;
				echo '<h1>'.$this->player.' won!</h1><br>';
				return false;
			}
		}	

		public function give_status(){
			$this->check_for_win(0,0);
			return $this->status;
		}

		public function move_x($dir,$dice){
			if( $this->x >= 0  && $this->x <= Movement::MAX_X ){
				$this->x += $dir*$dice;
			}
			else{
				echo 'You have reached board limits!.';
				$this->x = intval($this->x/2);
				$this->y = intval($this->y/2);
			}
		}

		public function move_y($dir,$dice){
			if( $this->y >= 0  && $this->y <= Movement::MAX_Y ){		
				$this->y += $dir*$dice;
			}
		}	

		public function give_position(){
			echo '<span style="color:'.$this->player.'">';
			echo $this->player.' pawn\'s position now is ('.$this->x.', '.$this->y.')';
			echo '</span><br>';
		}

}

/*********************GAME**************************/

$pawn_1 = new Pawn('Blue');
$pawn_2 = new Pawn('Red');
$total_moves = 1;

for($i=0; $i<$total_moves; $i+=1){

	if( $pawn_1->give_status() && $pawn_2->give_status() ){
		echo '<u>Move :'.$total_moves.'</u><br>';

		$pawn_1->move_x( mt_rand(-1,1), mt_rand(1,10) );
		$pawn_1->move_y( mt_rand(-1,1), mt_rand(1,10) );	
		$pawn_1->give_position();

		$pawn_2->move_x( mt_rand(-1,1), mt_rand(1,10) );
		$pawn_2->move_y( mt_rand(-1,1), mt_rand(1,10) );	
		$pawn_2->give_position();

		$total_moves+=1;
	}
	else{
		break;
	}
}


?>

Traits

Are a mechanism for code reuse

Reuse sets of methods freely in several independent classes living in different class hierarchies.
You cannot instansiate a Trait on its own.
An inherited member is overridden by a Trait member.
The current class overrides Trait methods.
Can use multiple Traits.(use trait1, trait2, trait3;)
If two Traits insert a method with the same name, a fatal error is produced if the conflict is not exlicitly resolved.(Resolved with insteadof/as)
Traits can be composed partially or entirely from other Traits.
Support of abstract methods in Traits.
Can define static members and methods.
Can define properties. The class cannot define a property with the same name.

<?php

trait MyPrefix{
	public function with_prefix(){
		return 'my_prefix_';
	}
}

trait MyDB{
	public function choose_db(){
		return 'innoDB';
	}
}

class Table{
	use MyPrefix, MyDB;
	public function create_table($name){
		$prefix = $this->with_prefix();
		$db = $this->choose_db();
		echo $prefix.$name.' table was created with the engine of '.$db;
	}
}

$t = new Table();
$t->create_table('persons');

?>

Overloading(interpreter hooks)

Overloading in PHP means to dynamically “create properties and methods”.

Property overloading

public void __set(string $name, mixed $value)

Is run when writing data to inaccessible properties.

public mixed __get(string $name)

Is utilized for reading data from inaccessible properties.

public bool __isset(string $name)

Is triggered by calling isset() or empty() on inaccessible properties.

public void __unset(string $name)
$name: the name of the prop. being interacted with.
$value: sp. the value the $name’ed property should be set to.
Property overloading only works in object context. Therefore these methods should not be declared static.

<?php

class History{

	private $data = array();

	public function __set($name, $value){
		if( array_key_exists($name, $this->data) ){
			echo "Updating $name to $value<br>";
		}
		else{
			echo "Setting $name to $value<br>";
		}
		$this->data[$name] = $value;
	}

	public function __get($name){
		echo "Property '$name' ";
		if (array_key_exists($name, $this->data)) {
			echo 'was returned.<br>';
			return $this->data[$name];
		}
		else{
			echo 'does not exist!<br>';
			return NULL;
		}
	}

	public function __isset($name){
		echo "Is '$name' set?<br>";
		if( isset($this->data[$name]) ){
			echo 'Yes it is!<br>';
		}
		else{
			echo 'No it\'s not!<br>';
		}
	}

	public function __unset($name){
		if (array_key_exists($name, $this->data)) {   
			echo "Unsetting '$name'<br>";
			unset($this->data[$name]);
		}
		else{
			echo "Cannot unset property '$name'. It does not exist!<br>";
		}
	}

}

$h = new History();
$h->Greek_Revolution = 1821;
$h->Not_Set;
isset($h->Not_Set);
unset($h->Greek_Revolution);

?>

Method overloading

The overloading methods are invoked when interacting with properties or methods that have not been declared or are not visible in the current scope. Must be defined as public.

public mixed __call(string $name, array $arguments)
public static mixed __callStatic(string $name, array $arguments)
$name: name of the method beeing called.
$arguments: an enumerated array containing the parameters passes to the $name’ed method.

<?php

class Animal{
	public function __call($name, $arguments){
		echo "Animal '$name' name ".$arguments[0]." age ".$arguments[1].
		" number of children ".$arguments[2].".<br>";
	}

	public static function __callStatic($name, $arguments){
		echo "Animal '$name' characteristics:<br>". implode('<br>', $arguments). "<br>";
	}
}

$animal = new Animal();

$animal->elephant('Sara',34,3);
$animal::elephant('Weigh: 7,000 kg',
'Tush: 1.8 meters',
'Proboscis: 2 meters',
'Teeth: 26',
'Lifetime: 60-70years');

?>

Object Iteration

foreach statement will iterate through visible properties by default.
If foreach used in a class method it will output all properties.(public, private, protected)
The Iterator interface may be implemented to allow the object to dictate how it will be iterated and what values will be available on each iteration.
The Iterator Aggregate interface can be used.

<?php

class MyClass{
    public $var1 = 'value 1';
    public $var2 = 'value 2';
    public $var3 = 'value 3';

    protected $protected = 'protected var';
    private   $private   = 'private var';

    function iterateVisible() {
       echo "MyClass::iterateVisible:<br>";
       foreach ($this as $key => $value) {
           print "$key => $value<br>";
       }
    }
}

$class = new MyClass();

foreach($class as $key => $value) {
    print "$key => $value<br>";
}
echo "<hr>";

$class->iterateVisible();

/****OUTPUT********
var1 => value 1
var2 => value 2
var3 => value 3

MyClass::iterateVisible:
var1 => value 1
var2 => value 2
var3 => value 3
protected => protected var
private => private var
******************/

?>

Comparing Objects

Two instances of the same class

o1==o2: TRUE
o1===o2: FALSE

Two referances of the same instance

o1==o2: TRUE
o1===o2: TRUE

Instances of two different classes

o1==o2: FALSE
o1===o2: FALSE

extensions can define own rules for their objects comparison

Type Hinting

<?php
class C {}
class D extends C {}
class E {}

function f(C $c) { //type hinting
    echo get_class($c)."\n";
}

f(new C);
f(new D);
f(new E); //error
?>

Late Static Bindings

“Late binding” comes from the fact that static:: will not be resolved using the class where the method is defined but it will rather be computed using runtime information.
Called a “static binding” as it can be used for (but is not limited to) static method calls.
Work by storing the class named in the last “non-forwarding call”.
In case of static method calls, this is the class explicitly named.(usually the one on the left of the :: operator)
In case on non-static method calls, it is the class of the object.
A “forwarding call” is a static one that is introduced by self::, parent::, static:: or by going up in the class hierarchy. forward_static_call()
get_called_class() can be used to retrieve the name of the called class and static introduces it’s scope.

<?php

class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        self::who();
		static::who();//late static binding
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();


//In non-static contexts, the called class will be the class of the object instance. 
//Since $this-> will try to call private methods from the same scope, using static:: 
//may give different results.Another difference is that static:: can only refer to 
//static properties. 
 
class A {
    private function foo() {
        echo "success!\n";
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
   /* foo() will be copied to B, hence its scope will still be A and
    * the call be successful */
}

class C extends A {
   private function foo(){
    /* original method is replaced; the scope of the new one is C */
    } 
}

$b = new B();
$b->test();
$c = new C();
$c->test();   //fails
 
 
//Late static bindings' resolution will stop at a fully resolved static call with no fallback. 
//On the other hand, static calls using keywords like parent:: or self:: will forward the calling information.

class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo();
        self::foo();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __CLASS__."\n";
    }
}

C::test();


?>