PHP Notes - Classes
PHP Objects / Classes
PHP includes a complete object model. Some of its features are: visibility, abstract and final classes and methods, additional magic methods, interfaces, and cloning. PHP treats objects in the same way as references or handles, meaning that each variable contains an object reference rather than a copy of the entire object.
<?php class User { // PROPERTY DECLARATION private $user_id = -1; private $user_status = 0; private $user_access = -1; private $user_name = ""; // CONSTRUCTOR public function __construct(int $id, int $status, int $access, string $name) { $this->$user_id = $id; $this->$user_status = $status; $this->$user_access = $access; $this->$user_name = $name; } // METHOD DECLARATION public function displayName() { echo $this->user_name; } } $user = new User(0, 1, 0, "Dirk"); $user->displayName(); ?>
Class Constants
It is possible to define constants on a per-class basis remaining the same and unchangeable.
The default visibility of class constants is public.
Class constants can be redefined by a child class.
As of PHP 8.1.0, class constants cannot be redefined by a child class if it is defined as final.
- It's also possible for interfaces to have constants.
- It's possible to reference the class using a variable. The variable's value can not be a keyword (e.g. self, parent and static).
- Class constants are allocated once per class, and not for each class instance.
class Employee { private const weeks_per_year = 52; protected const days_per_year = 260; // EXCLUDES WEEKENDS = 52 x 5 = 260 public const hours_per_week = 40; public const hours_per_day = 8; public const months_per_year = 12; private $first_name = ""; private $middle_name = ""; private $last_name = ""; private $yearly_salary = 0; public function __construct(string $first, string $middle, string $last, int $salary) { $this->first_name = $first; $this->middle_name = $middle; $this->last_name = $last; $this->yearly_salary = $salary; } public function getName() { return $this->first_name ." ". $this->middle_name ." ". $this->last_name; } public function getYearly() { return $this->yearly_salary; } public function getMonthly() { $monthly = $this->yearly_salary / self::months_per_year; return $monthly; } public function getWeekly() { $weekly = $this->yearly_salary / self::weeks_per_year; return $weekly; } public function getDaily() { $daily = $this->yearly_salary / self::days_per_year; return $daily; } public function getHourly() { // $hourly = $this->getDaily() / self::hours_per_day; return $hourly; } } $emp1 = new Employee("Fred", "T", "Garvin", 140000); echo $emp1->getName(); echo number_format($emp1->getYearly(),2,".",","); echo number_format($emp1->getMonthly(),2,".",","); echo number_format($emp1->getWeekly(),2,".",","); echo number_format($emp1->getDaily(),2,".",","); echo number_format($emp1->getHourly(),2,".",",");
Name | Fred T Garvin |
---|---|
Yearly | $140,000.00 |
Monthly | $11,666.67 |
Weekly | $2,692.31 |
Daily | $538.46 |
Hourly | $67.31 |
Autoloading Classes
Many developers writing object-oriented applications create one PHP source
file per class definition. One of the biggest annoyances is having to write
a long list of needed includes at the beginning of each script (one for each class).
The spl_autoload_register() function registers any number of autoloaders,
enabling for classes and interfaces to be automatically loaded if they are
currently not defined. By registering autoloaders, PHP is given a last chance
to load the class or interface before it fails with an error.
Any class-like construct may be autoloaded the same way. That includes classes,
interfaces, traits, and enumerations.
spl_autoload_register() may be called multiple times in order to register multiple autoloaders.
Throwing an exception from an autoload function, however, will interrupt that process and not
allow further autoload functions to run.
For that reason, throwing exceptions from an autoload function is strongly discouraged.
Constructors & Destructors
class myClass { ... public function __construct(...) { // CODE HERE } ... } $myClass = new MyClass(...);
Constructors & Inheritance
class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } class OtherSubClass extends BaseClass { // inherits BaseClass's constructor } // In BaseClass constructor $obj = new BaseClass(); // In BaseClass constructor // In SubClass constructor $obj = new SubClass(); // In BaseClass constructor $obj = new OtherSubClass();
// ALL ALLOWED: static $x = new Foo; const C = new Foo; function test($param = new Foo) { ... } #[AnAttribute(new Foo)] class Test { public function __construct( public $prop = new Foo, ) {} } // ALL NOT ALLOWED (COMPILE-TIME ERROR): function test( $a = new (CLASS_NAME_CONSTANT)(), // DYNAMIC CLASS NAME $b = new class {}, // ANONYMOUS CLASS $c = new A(...[]), // ARGUMENT UNPACKING $d = new B($abc), // UNSUPPORTED CONSTANT EXPRESSION ) {}
Static Creation Methods
PHP only supports a single constructor per class. In some cases, however, it may be desirable to allow an object to be constructed in different ways with different inputs. The recommended way to do so is by using static methods as constructor wrappers.
Declaring class properties or methods as static makes them accessible without needing an instantiation of the class. These can also be accessed statically within an instantiated class object.
Because static methods are callable without an instance of the object created, the pseudo-variable $this is not available inside methods declared as static.
class Product { private ?int $id; private ?string $name; // CONSTRUCTOR FUNCTION private function __construct(?int $id = null, ?string $name = null) { $this->id = $id; $this->name = $name; } public static function fromBasicData(int $id, string $name): static { $new = new static($id, $name); return $new; } public static function fromJson(string $json): static { $data = json_decode($json); return new static($data['id'], $data['name']); } public static function fromXml(string $xml): static { // Custom logic here. $data = convert_xml_to_array($xml); $new = new static(); $new->id = $data['id']; $new->name = $data['name']; return $new; } } $p1 = Product::fromBasicData(5, 'Widget'); $p2 = Product::fromJson($some_json_string); $p3 = Product::fromXml($some_xml_string);
5 - Widget
Constructor Promotion
As of PHP 8.0.0, constructor parameters may also be promoted to correspond to an object property. It is very common for constructor parameters to be assigned to a property in the constructor but otherwise not operated upon. Constructor promotion provides a short-hand for that use case.
<?php class Point { public function __construct(protected int $x, protected int $y = 0) { } }
When a constructor argument includes a modifier, PHP will interpret it as both an object property and a constructor argument, and assign the argument value to the property. The constructor body may then be empty or may contain other statements. Any additional statements will be executed after the argument values have been assigned to the corresponding properties.
Not all arguments need to be promoted. It is possible to mix and match promoted and not-promoted arguments, in any order. Promoted arguments have no impact on code calling the constructor.
The question mark preceding the parameter type. This is known as a Nullable Type. Type declarations for parameters and return values can now be marked as nullable by prefixing the type name with a question mark. This signifies that as well as the specified type, NULL can be passed as an argument, or returned as a value, respectively.
class MyDestructableClass { function __construct() { print "In constructor\n"; } function __destruct() { print "Destroying " . __CLASS__ . "\n"; } } $obj = new MyDestructableClass();
Visibility
Class properties may be defined as public, private, or protected. Properties declared without any explicit visibility keyword are defined as public.
public | Visible to the public. Can be refered to directly. |
---|---|
protected | Visible to the class and to any class descendants. |
private | Visible only within the class itself. Not visible to descendant classes. |
<?php class MyClass { public $public = 'Public'; protected $protected = 'Protected'; private $private = 'Private'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj = new MyClass(); echo $obj->public; // WORKS echo $obj->protected; // FATAL ERROR echo $obj->private; // FATAL ERROR $obj->printHello(); // SHOWS PUBLIC, PROTECTED AND PRIVATE class MyClass2 extends MyClass { // We can redeclare the public and protected properties, but not private public $public = 'Public2'; protected $protected = 'Protected2'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj2 = new MyClass2(); echo $obj2->public; // WORKS echo $obj2->protected; // FATAL ERROR echo $obj2->private; // UNDEFINED $obj2->printHello(); // SHOWS PUBLIC2, PROTECTED2, UNDEFINED ?>
<?php class MyClass { public function __construct() { } // DECLARE A PUBLIC CONSTRUCTOR public function MyPublic() { } // DECLARE A PUBLIC METHOD protected function MyProtected() { } // DECLARE A PROTECTED METHOD private function MyPrivate() { } // DECLARE A PRIVATE METHOD function Foo() { // THIS IS PUBLIC $this->MyPublic(); $this->MyProtected(); $this->MyPrivate(); } } $myclass = new MyClass; $myclass->MyPublic(); // WORKS $myclass->MyProtected(); // FATAL ERROR $myclass->MyPrivate(); // FATAL ERROR $myclass->Foo(); // PUBLIC, PROTECTED AND PRIVATE WORK class MyClass2 extends MyClass { function Foo2() { // THIS IS PUBLIC $this->MyPublic(); $this->MyProtected(); $this->MyPrivate(); // FATAL ERROR } } $myclass2 = new MyClass2; $myclass2->MyPublic(); // WORKS $myclass2->Foo2(); // PUBLIC AND PROTECTED WORK, NOT PRIVATE class Bar { public function test() { $this->testPrivate(); $this->testPublic(); } public function testPublic() { echo "Bar::testPublic\n"; } private function testPrivate() { echo "Bar::testPrivate\n"; } } class Foo extends Bar { public function testPublic() { echo "Foo::testPublic\n"; } private function testPrivate() { echo "Foo::testPrivate\n"; } } $myFoo = new Foo(); $myFoo->test(); // Bar::testPrivate // Foo::testPublic ?>
lt;?php class MyClass { public const MY_PUBLIC = 'public'; // DECLARE A PUBLIC CONSTANT protected const MY_PROTECTED = 'protected'; // DECLARE A PROTECTED CONSTANT private const MY_PRIVATE = 'private'; // DECLARE A PRIVATE CONSTANT public function foo() { echo self::MY_PUBLIC; echo self::MY_PROTECTED; echo self::MY_PRIVATE; } } $myclass = new MyClass(); MyClass::MY_PUBLIC; // WORKS MyClass::MY_PROTECTED; // FATAL ERROR MyClass::MY_PRIVATE; // FATAL ERROR $myclass->foo(); // PUBLIC, PROTECTED AND PRIVATE WORK class MyClass2 extends MyClass { // THIS IS PUBLIC function foo2() { echo self::MY_PUBLIC; echo self::MY_PROTECTED; echo self::MY_PRIVATE; // FATAL ERROR } } $myclass2 = new MyClass2; echo MyClass2::MY_PUBLIC; // WORKS $myclass2->foo2(); // PUBLIC AND PROTECTED WORK, NOT PRIVATE ?>
Inheritance
When extending a class, the subclass inherits all of the public and protected methods, properties and constants from the parent class. Unless a class overrides those methods, they will retain their original functionality.
This is useful for defining and abstracting functionality, and permits the implementation of additional functionality in similar objects without the need to reimplement all of the shared functionality.
Private methods of a parent class are not accessible to a child class. As a result, child classes may reimplement a private method themselves without regard for normal inheritance rules. As of PHP 8.0.0, the only private method restriction that is enforced is private final constructors, as that is a common way to "disable" the constructor when using static factory methods instead.
The visibility of methods, properties and constants can be relaxed, e.g. a protected method can be marked as public, but they cannot be restricted, e.g. marking a public property as private. An exception are constructors, whose visibility can be restricted, e.g. a public constructor can be marked as private in a child class.
class Person { private string $first_name; private string $middle_name; private string $last_name; public function __construct(string $first, string $middle, string $last) { $this->first_name = $first; $this->middle_name = $middle; $this->last_name = $last; } public function getFullName() { return $this->first_name .' '. $this->middle_name .' '. $this->last_name; } } class SalaryEmployee extends Person { private int $employee_id; private int $employee_salary; public function __construct(int $id, int $salary, string $first, string $middle, string $last) { parent::__construct($first, $middle, $last); $this->employee_id = $id; $this->employee_salary = $salary; } public function showEmployee() { $return_str = parent::getFullName() ."<br/>"; $return_str .= "ID: ". $this->employee_id ."<br/>"; $return_str .= "Salary: ". number_format($this->employee_salary,2,".",",") ."<br/>"; echo $return_str; } } $Employee1 = new SalaryEmployee(12,140000,"John","L","Doe"); $Employee1->showEmployee();
John L DoeID: 12
Salary: 140,000.00
Need To Extend A Base Class
I have a problem with a lack of functionality of the SimpleXMLElement class. It does not have a means to write out CDATA encapsulated text to an XML node. There are many examples of creating a node via a call to dom_import_simplexml of the DOMElement class which has a built in method called createCDATASection(cdata_text)
In the code that I need this functionality, the means of getting the XML is from a file. A function of the SimpleXMLElement class is called without actually declaring a class variable, but by simply calling the simplexml_load_file method which returns a class instance. Example below:
if (file_exists("xml_users.xml")) { $xml = simplexml_load_file("xml_users.xml") or die ("Cannot open XML file"); // CHECK FOR EXISTING RECORD WITH ENTERED USERNAME foreach($xml->children() as $users) {...} ... }
Scope Resolution Operator
Static Keyword
Class Abstraction
Object Interfaces
Traits
Anonymous Classes
Overloading
Object Iteration
Magic Methods
Final Keyword
Object Cloning
Comparing Objects
Late Static Bindings
Objects and references
Object Serialization
Covariance & Contravariance
OOP Changelog