PHP – Language Details Martin Kruliš by Martin Kruliš (v1.0) 12.12.2018
Revision of PHP Fundamentals Dynamic Nature of PHP Dynamic weakly-typed language Variables Mnemonic references to values No declarations, created on the first assignment In the global or local scope Globals can be mapped into local context (global keyword) No explicit type (type is determined by current value) Type can be changed with a new assignment Existence can be tested (isset) and terminated (unset) Automatic casting (type juggling) There are very few good applications for global keyword. Use it carefully. Furthermore, note that the global keyword enforces explicit mapping of external (global) variables into local scope, which promotes the idea of writing more coherent functions (i.e., functions that depend only on data passed by arguments). by Martin Kruliš (v1.0) 12.12.2018
However, it is better to traverse the arrays using foreach Array in PHP is an ordered map of key-value pairs I.e., maintaining order independent on keys Key provides quick access to value Keys can be integers or strings Array holds internal pointer for iteration reset($array) – rewinds the pointer end($array) – sets the pointer to the last element next($array), prev($array) – move the pointer key($array), current($array) – actual key/value However, it is better to traverse the arrays using foreach by Martin Kruliš (v1.0) 12.12.2018
Arrays Sorting Merging Tokenization Destructive assignment Slightly more complicated – many combinations sort(), asort(), ksort(), usort(), uksort(), … Merging $res = array_merge($a, $b); Tokenization explode($delim, $str) implode($glue, $array) Destructive assignment list($a, $b, $c) = [1, 2, 3]; Note that even the sort() function may look like obvious choice, it may not be what you wanted since it does not maintain key-value association (values are sorted and new numeric indices are provided for them). by Martin Kruliš (v1.0) 12.12.2018
[ 1=>'a', 4=>'b', 5=>'c', 42=>'d', 43=>'e' ] Arrays All keys will be cast to 1 $a == [ 1 => 'd' ] Pitfalls $a = [ 1=>'a', '1'=>'b', 1.5=>'c', true=>'d' ]; $a = [ 'a', 4 => 'b', 'c' ]; $a[42] = 'd'; $a[] = 'e'; (array)$scalarValue == [ $scalarValue ] (array)null == [] Next index is last index + 1 (but at least 0) [ 1=>'a', 4=>'b', 5=>'c', 42=>'d', 43=>'e' ] Strings are cast to ints if they hold valid decimal integers. However ‘+42’ or ‘0123’ are not considered to be valid decimal integers. Scalar values are casted to single-item arrays, except for null by Martin Kruliš (v1.0) 12.12.2018
Variable Variables Indirect Access to Values Name of one variable is stored in another variable $a = 'b'; $$a = 42; // the same as $b = 42; $a = 'b'; $b = 'c'; $c = 'd'; $$$$a = 'hello'; // the same as $d = 'hello'; The {,} can be used to avoid ambiguous situations Can be used with members, functions, classes, … $fncName = 'foo'; $fncName(); // same as foo() by Martin Kruliš (v1.0) 12.12.2018
References References $a int (2) int (1) $b Similar to Unix hard-links in FS Multiple variables attached to the same data Reference is taken by the & operator Independent on object references A reference to an object can be created $a = 1; $b = &$a; $b++; echo $a; // prints 2 $a int (2) int (1) $b by Martin Kruliš (v1.0) 12.12.2018
References in Functions Arguments as References Similar usage as var keyword in Pascal function inc(&$x) { $x++; } Returning References function &findIt($what) { global $myArray; return &$myArray[$what]; This is perhaps the most practical utilization of references Useful applications are rather limited by Martin Kruliš (v1.0) 12.12.2018
References References vs. Pointers The unset() Function $x = 42; foo($x); How is $x affected? References vs. Pointers function foo(&$var) { $var = &$GLOBALS['bar']; } The unset() Function Does not remove data, only the variable Data are removed when not referenced The global Declaration global $a; $a = &$GLOBALS['a']; In the first example, the $var is reassigned to be a new reference (referring to global variable $bar) whilst the $x remains unaffected. If references were more pointer-like (like in C++), the $x would be assigned a reference to global $bar. by Martin Kruliš (v1.0) 12.12.2018
References Pitfalls There are too many… $array = [ 42, 54, 19 ]; foreach ($array as &$value) { ++$value; } unset($value); ... foreach ($array as $value) { echo $value; The reference lingers on… And here we assign to it again Do not reuse variables. Especially, if they are references. Now, the $array holds [ 43, 55, 55 ] by Martin Kruliš (v1.0) 12.12.2018
Revising PHP Functions Declaration Keyword function followed by the identifier function foo([args, …]) { … body … } Function body Pretty much anything (even a function/class decl.) Nested functions/classes are declared once the function is called for the first time Functions are 2nd level citizens and identifier space is flat Type Hinting Dynamic type checks for arguments and return value Note: The identifier namespace is indeed flat; however, we have explicit namespaces. I.e., the space is flat within each explicit (or default) namespace. by Martin Kruliš (v1.0) 12.12.2018
Function Arguments Argument Declarations Variable Number of Arguments Implicit values may be provided function foo($x, $y = 1, $z = 2) { … } Arguments with implicit values are aligned to the right Note that PHP functions does not support overloading Variable Number of Arguments func_num_args(), func_get_arg(), func_get_args() function foo($a, $b, ...$rest) $rest gets remaining arguments as an array Splat operator (…) works also for argument unpacking $args = [1, 2]; foo(...$args); // foo(1,2); by Martin Kruliš (v1.0) 12.12.2018
Variable Functions Indirect Calling Similar Constructs Calling a function by its name stored in a variable function foo($x, $y) { … } $funcName = 'foo'; $funcName(42, 54); // the same as foo(42, 54); Similar Constructs Using specialized invocation functions call_user_func('foo', 42, 54); call_user_func_array('foo', array(42, 54)); by Martin Kruliš (v1.0) 12.12.2018
Anonymous Functions Anonymous Functions 𝑓 𝑥 = 𝑒 𝑥 Anonymous Functions $fnc = function($arg) { …body… }; $fnc(42); The anonymous function is an instance of Closure It can be passed on like an object The visible variables must be explicitly stated $fnc = function(…) use ($var, &$refvar) { … }; These variables are captured in the closure Variables passed by reference can be modified Nameless/Lambda functions (obsolete) $mul = create_function('$x, $y', 'return $x*$y'); PHP is not like JavaScript. Note that the anonymous functions do not work as methods: $obj = new StdClass(); $obj->fnc = function() { … }; Then $obj->fnc(); will not work, since fnc is not a method of StdClass. However, $fnc = $obj->fnc; $fnc(); will work as expected. by Martin Kruliš (v1.0) 12.12.2018
Anonymous Functions Example $shopping_list = [ 'milk', 'spam', 'eggs', 'beer', '' ]; array_walk($shopping_list, function($item) { echo "$item\n"; }); function countValidItems(array $list){ $count = 0; $callback = function($item) use (&$count) { if ($item) ++$count; }; array_walk($list, $callback); return $count; } echo "Valid Items: ", countValidItems($shopping_list); Invoked once for every item Explicitly captured external variable (by reference) by Martin Kruliš (v1.0) 12.12.2018
Object Oriented PHP First attempt made in PHP 4 Poor design, failed Current implementation in PHP 5 and 7 Design is inspired by languages like Java or C# Adaptations for interpreted loosely-typed language E.g., there are no “virtual” methods Powerful in combination with PHP-specific features For instance with variable variables: $obj = new $className(); $obj->$methodName(); by Martin Kruliš (v1.0) 12.12.2018
Syntax Basics member visibility class Foo { public $var = 0; // a member variable public function bar() { // a method echo $this->var; } $instance = new Foo(); // create new instance $instance->var = 42; $instance->bar(); $instance = null; by Martin Kruliš (v1.0) 12.12.2018
Objects & References Objects Are Reference Types Like in Java or C# class Foo { public $bar = 0; public function __construct($b) { $this->bar = $b; } } $foo1 = new Foo(10); $foo2 = $foo1; $foo3 = &$foo1; $foo2 = new Foo(20); $foo3 = new Foo(30); $foo2 objref#2 objref#1 Foo(20) $foo1 objref#3 objref#1 Foo(10) $foo3 Foo(30) by Martin Kruliš (v1.0) 12.12.2018
Member Variables Member Visibility Type (is mandatory) public – accessible from anywhere protected – access. from within and derived classes private – only accessible from within the class Implicit Member Declaration Created as public by default class Foo { private $bar; } $foo = new Foo(); $foo->bar = 1; // Error! 'bar' is private $foo->barbar = 42; // OK, new member is created Note: In older versions of PHP (PHP 4 to be exact), the member variables were declared by "var" keyword. PHP 5 still recognizes var for compatibility reasons and such members are treated as public. by Martin Kruliš (v1.0) 12.12.2018
Member Variables Member Variables are Dynamic Why declare members in classes? Visibility control Default values (initialization) Better readability (documentation comments, …) Reflection class Point { public $x = 1; public $y = 2; } $p = new Point(); $p = new stdClass(); $p->x = 1; $p->y = 2; vs. Base class without any members (used also for implicit object constructions) For instance, when array is converted to an object, the class of that object is stdClass, whilst array keys become the member variables names. by Martin Kruliš (v1.0) 12.12.2018
Member Functions (Methods) Visibility Type The same meaning as for variables Optional, set as public if missing Accessing the Object Instance Current object instance is available through $this It must be used to access member variables To distinguish members from local variables Otherwise the Same as Regular Functions No overloading, variadic, … by Martin Kruliš (v1.0) 12.12.2018
Inheritance Standard Inheritance Model Overriding Methods Each class may have only one parent class Multi-inheritance is achieved by interfaces and traits Overriding Methods All methods act as if they are virtual parent::method() – calling overridden version AncestorClass::method() – calling explicit version Methods tagged as final cannot be overridden class MyFoo extends Foo { public function Bar() { parent::Bar(); } } by Martin Kruliš (v1.0) 12.12.2018
Constructor Special Method __construct() Used to initialize the object Called automatically (by the new operator) May have arguments Same behavior as regular method (e.g., no overloading) Does not have to be defined Parent’s constructor or implicit constructor is used Parent’s constructor is not called implicitly Constructor should be public Except for some special cases like Singleton or Factory Method design patterns Fact that “Parent’s constructor is not called implicitly” is not typical for OO languages (e.g., C++ or C# calls parent constructors implicitly when possible or enforce explicit call). by Martin Kruliš (v1.0) 12.12.2018
Destructor Special Method __destruct() Called when the object is garbage-collected When ref. count reaches 0 or at the end of the script Does not have to be defined Parent’s destructor or implicit destructor is used Destructor should not throw exceptions Since they may not be handled properly Parent’s destructor is not called implicitly Destructor should be public And there are no reasonable exceptions by Martin Kruliš (v1.0) 12.12.2018
Member Constants Member Variables with Constant Values Declared by const prefix No visibility declaration, treated as public Accessed from class using :: operator By class name or by self identifier from within class Foo { const BAR = 42; function echoBar() { echo self::BAR; } } echo Foo::BAR; Most programmers tend to use upper case identifiers for constants (both global and class constants). by Martin Kruliš (v1.0) 12.12.2018
Static Members Revision Static vs. Regular (Dynamic) Members Class static variable/constant static method new operator method Object member variable logically belongs to (is stored at) is used (called) on by Martin Kruliš (v1.0) 12.12.2018
Static Members Static (Class) Members Declared by static keyword before the member Accessed by :: operator (like constants) E.g., MyClass::$statVar or MyClass::myFunc() One instance, no matter how many objects class has I.e., static methods does not have $this The same types of visibility as regular members Small differences in inheritance class A { public static $x; } class B extends A { public static $x; } class C extends A {} … C::$x = 42; new variable same as A::$x by Martin Kruliš (v1.0) 12.12.2018
Static Members Example Only limited number of instances may exist class Prescious { const MAX_INSTANCES = 2; private static $instances = 0; public static function create() { if (self::$instances < self::MAX_INSTANCES) return new Foo(); else return null; } private function __construct() { ++self::$instances; ... } public function __destruct() { --self::$instances; ... } ... } $foo1 = Foo::create(); $foo2 = Foo::create(); $foo3 = Foo::create(); $foo3 is null by Martin Kruliš (v1.0) 12.12.2018
Abstract Entities Abstract Classes and Methods Prefixed with keyword abstract Abstract class cannot be instantiated Abstract method has no body It is expected to be implemented in derived class abstract class AbstractClass { abstract function foo(); } class ConcreteClass extends AbstractClass { function foo() { … foo body … } $obj = new ConcreteClass(); by Martin Kruliš (v1.0) 12.12.2018
Interfaces Interfaces List of public methods a class must implement Interfaces may be extended like classes Using the extends operator interface IFoo { public function bar($goo); } class Foo implements IFoo { public function bar($goo) { ... Prior to PHP 5.3.9, a class could not implement two interfaces that specified a method with the same name, since it would cause ambiguity. More recent versions of PHP allow this as long as the duplicate methods have the same signature. Unlike in case of inheritance, a class may implement multiple interfaces by Martin Kruliš (v1.0) 12.12.2018
Object Iterators Iterating Member Variables By foreach construct (like arrays) Keys are strings with the name of the member Only visible (accessible) members are iterated class MyClass { public $var1 = 1; public $var2 = 2; private $var3 = 3; } $obj = new MyClass(); foreach ($obj as $key => $value) { ... } Custom iteration can be implemented Interface Iterator and IteratorAggregate Both Iterator and IteratorAggregate interfaces extend abstract Traversable interface, which is empty, but it can be used to test an object, whether it has a custom iterator (by the means of instanceof operator). by Martin Kruliš (v1.0) 12.12.2018
Object Cloning Copying Reference vs. Copying Object Assignment copies reference, not the object Object copy must be invoked explicitly, by cloning $foo = new Foo(); $foo2 = $foo; $foo3 = clone $foo; Foo object #1 Note that human cloning is prohibited by law in many countries (https://en.wikipedia.org/wiki/Human_cloning). $foo $foo2 Foo object #2 $foo3 by Martin Kruliš (v1.0) 12.12.2018
$this is newly copied object, which has all members already assigned Object Cloning Shallow vs. Full Copy Cloning process creates shallow copy by default Assignment operator is used on every member Post-cloning operations may be implemented in method __clone(), which is invoked on the copy public function __clone() { $this->innerObj = clone $this->innerObj; } $this is newly copied object, which has all members already assigned by Martin Kruliš (v1.0) 12.12.2018
Magic Methods Member Variables Accessors __get() – control read-access to members __set() – control write-access to members __isset() – isset() override for members __unset() – unset() override for members Overrides access to member variables, which are not declared or not visible Declared variables are accessed directly Only for regular members, not for static Handle with care They may lead to less readable code or even errors Implementing magic methods is sometimes called “overloading” in PHP. Do not mix-up with regular overloading that exists in other languages. There is some discussion whether using this magic assessors is actually good or bad. On one side, they may shorten your code (let you express more complex things with less writing). On the other hand, they could be slower in execution and they may create more obfuscated code (documenting magic members is tedious, there could be no autocompletion in your IDE, etc.). The following two examples show good (justifiable) applications of accessors: You have an object that carries name-value pairs and you want the user to read the values easily, but not to be able to modify them (or only via methods). Mark all values private and write a __get method, which provides the access to them even from outside of the object. Absence of __set method will result in error when anyone wants to write the properties from the outside. You have a generic class/object, that represents e.g., a node in a complex tree structure (like XML). The accessors can be used to read or modify the structure itself (see the embedded SimleXMLElement class). Otherwise it might be better to use regular methods to achieve what you are doing/could do with magic methods. by Martin Kruliš (v1.0) 12.12.2018
Magic Methods Method Invocation Override Array Access Interface __call() – intercepts calls to not visible methods __callStatic() – the same for static methods __invoke() – when object is called as function Array Access Interface Allows using the object as an array ($obj[…]) boolean offsetExists(mixed $offset) mixed offsetGet(mixed $offset) void offsetSet(mixed $offset, mixed $value) void offsetUnset(mixed $offset) by Martin Kruliš (v1.0) 12.12.2018
Magic Methods Example class Readonly { private $foo; ... public function __construct($foo) { $this->foo = $foo; } public function __get($name) { return (isset($this->$name)) ? $this->$name; : null; } public function __isset($name) { return isset($this->$name); public function __set($name, $value) { throw new Exception("Object is read only!"); public function __unset($name) { } by Martin Kruliš (v1.0) 12.12.2018
Comparing Objects Reference Comparison Behavior $object1 == $object2 True if both object are of the same class and all member variables are equal (==) $object1 === $object2 True if both variables hold a reference to exactly the same object Behavior of != and !== operators can be easily extrapolated by Martin Kruliš (v1.0) 12.12.2018
Type Detection/Verification Operator instanceof Verifies whether object is an instance of given class or derived class, or implements given interface if ($foo instanceof FooClass) ... Functions Testing Types get_class() – returns class name as string get_parent_class() – name of the parent class is_a() – verifies that object is of given class is_subclass_of() – like is_a(), but checks also derived classes by Martin Kruliš (v1.0) 12.12.2018
OOP-Related Functions Testing Existence class_exists(), interface_exists() method_exists() Listings get_declared_classes(), get_declared_interfaces() get_class_methods() get_object_vars() get_class_vars() Indirect Method Calls call_user_func_array(array($obj, 'methodName'), $params); $name = 'methodName'; $obj->$name(...$params) by Martin Kruliš (v1.0) 12.12.2018
Autoloading Automatic Loading of Classes Useful for libraries, reduces the number of includes Global function __autoload() Called automatically when undeclared class is accessed function __autoload($className) { if (file_exists("libs/$className.php")) include_once "libs/$className.php"; else log("Class $className is not defined!"); } spl_autoload_register() Register (possibly multiple) callback handler(s) The spl_autoload_register() is preferred in case of writing libraries. It allows multiple libraries to register their own autoloading function. There are also other spl_* functions that simplify autoloading. by Martin Kruliš (v1.0) 12.12.2018
Namespaces Namespaces Another way how to encapsulate space of identifiers Affect classes, traits, interfaces, functions, constants Similar to directories and files Declaration: namespace identifier; First statement in the file Identifier may be hierarchical (separator is backslash) Dereferencing myClass -> currentNS\myClass name\space\myClass -> currentNS\name\space\myClass \name\space\myClass – absolute path, no modifications Aliasing – use identifier [as identifier]; by Martin Kruliš (v1.0) 12.12.2018
Discussion by Martin Kruliš (v1.0) 12.12.2018