Appearance
Classes
The basics: class Student
- Open classes/Student.php
php
<?php
namespace classes;
class Student
{
private $name;
private $gender;
// public function __construct()
// {
// }
public function getGender(): string
{
return $this->gender;
}
public function getName(): string
{
return $this->name;
}
public function setGender(string $gender): void
{
$this->gender = $gender;
}
public function setName(string $name): void
{
$this->name = $name;
}
}
<?php
namespace classes;
class Student
{
private $name;
private $gender;
// public function __construct()
// {
// }
public function getGender(): string
{
return $this->gender;
}
public function getName(): string
{
return $this->name;
}
public function setGender(string $gender): void
{
$this->gender = $gender;
}
public function setName(string $name): void
{
$this->name = $name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- We assume you have (basic) knowledge on OOP (object-oriented programming) and concepts like classes, objects, constructors, mutators, ...
NAMING CONVENTIONS
- The name of a PHP class starts with a capital letter (
Student
) - The name of the class definition file equals the name of the class with the extension .php (Student.php)
- The class file starts with the statement
namespace classes;
, used to uniquely identify the classStudent
. By using namespaces, we can have a class with the same name (Student
) in another namespace.
In our example, the class definition file Student.php is located in the folder classes and, as such, the namespace corresponds to the folder structure. This approach (namespaces that correspond to the folder structure) is - although it's not mandatory in PHP - considered to be best practice.
TIP
If you generate a new PHP class (definition file) by right-clicking a folder in PhpStorm and choose New -> PHP Class, namespacing is automatically taken care of
- The class
Student
contains two class variables or properties ($name
and$gender
)- It's common practice to precede the properties of a class with the access modifier
private
, indicating that these properties are only accessible from within the class that defines it
- It's common practice to precede the properties of a class with the access modifier
- The class
Student
also contains four class functions or methods, i.e. two "getters" to get/retrieve the value of the properties and two "setters" to set/change the property values- These get and set methods are often referred to as mutator methods
- It's common practice to precede (get and set) methods with the access modifier
public
, indicating that these methods are also accessible from outside the class - Within (these) methods, the
$gender
and$name
properties are addressed as$this->gender
and$this->name
- The keyword
$this
refers to the calling/current object, i.e., the object to which the method belongs - The arrow symbol
->
is a PHP construction to access properties (and methods) of a given object
- The keyword
- When you use auto-completion to implement a setter, the return type
: void
will be added after the function name. This is a new, but not obliged feature since PHP 7.1. to emphasize that a function should not return any value.
- The constructor method
public function __construct()
is called when new objects (or instances) of theStudent
class are made (with the keywordnew
, see further on). If its implementation is not included (the code is commented out), it is created automatically. - Open course/classes_student.php
php
<article>
<?php
include_once '../classes/Student.php'; // include the class
use classes\Student; // use the namespace
$students[0] = new Student();
$students[0]->setName('John Doe');
$students[0]->setGender('M');
$students[1] = new Student();
$students[1]->setName('Jane Doe');
$students[1]->setGender('F');
foreach ($students as $i => $student) {
echo "<p>Name of student $i = {$student->getName()}</p>\n";
echo "<p>Gender of student $i = {$student->getGender()}</p>\n";
}
?>
</article>
<article>
<?php
include_once '../classes/Student.php'; // include the class
use classes\Student; // use the namespace
$students[0] = new Student();
$students[0]->setName('John Doe');
$students[0]->setGender('M');
$students[1] = new Student();
$students[1]->setName('Jane Doe');
$students[1]->setGender('F');
foreach ($students as $i => $student) {
echo "<p>Name of student $i = {$student->getName()}</p>\n";
echo "<p>Gender of student $i = {$student->getGender()}</p>\n";
}
?>
</article>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- We start the script by including the class definition file (
include_once '../classes/Student.php';
) - The
use
statementuse classes\Student;
(a shorthand foruse classes\Student as Student;
) defines an alias for the classStudent
, so that we can use the class without having to write its Fully Qualified Name\classes\Student
- Objects of the class
Student
are made with the keywordnew
, followed by the name of the class. The constructor method will be called. - The properties of these
Student
objects are filled in and retrieved using the public mutator (set and get) methods, which are accessed through the arrow operator->
REMARK
Notice the use of single curly braces ({$student->getName()}
) in order to isolate $student->getName()
from the surrounding text for interpolation (and get an error-free echo
statement)
A second example: class Teacher
- Open classes/employees/Teacher.php
php
<?php
namespace classes\employees;
class Teacher
{
private $name;
private $gender;
public function __construct(string $name, string $gender)
{
$this->name = $name;
$this->gender = $gender;
}
public function getName(): string
{
return $this->name;
}
public function getGender(): string
{
return $this->gender;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function setGender(string $gender): void
{
$this->gender = $gender;
}
}
<?php
namespace classes\employees;
class Teacher
{
private $name;
private $gender;
public function __construct(string $name, string $gender)
{
$this->name = $name;
$this->gender = $gender;
}
public function getName(): string
{
return $this->name;
}
public function getGender(): string
{
return $this->gender;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function setGender(string $gender): void
{
$this->gender = $gender;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- Open course/classes_teacher.php
php
<article>
<?php
include_once '../classes/employees/Teacher.php'; // include the class
use classes\employees\Teacher; // use the namespace
$teachers[0] = new Teacher('Michaël Cloots', 'M');
$teachers[1] = new Teacher('Jan Janssen', 'M');
$teachers[2] = new Teacher('Patrick Verhaert', 'M');
foreach ($teachers as $i => $teacher) {
echo "<p>Teacher $i = {$teacher->getName()} ({$teacher->getGender()})</p>\n";
}
?>
</article>
<article>
<?php
include_once '../classes/employees/Teacher.php'; // include the class
use classes\employees\Teacher; // use the namespace
$teachers[0] = new Teacher('Michaël Cloots', 'M');
$teachers[1] = new Teacher('Jan Janssen', 'M');
$teachers[2] = new Teacher('Patrick Verhaert', 'M');
foreach ($teachers as $i => $teacher) {
echo "<p>Teacher $i = {$teacher->getName()} ({$teacher->getGender()})</p>\n";
}
?>
</article>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- In the previous example, making and initializing a new (
Student
) object required three statements
php
$students[0] = new Student();
$students[0]->setName('John Doe');
$students[0]->setGender('M');
$students[0] = new Student();
$students[0]->setName('John Doe');
$students[0]->setGender('M');
1
2
3
2
3
- In this example, we coded this (making and initializing objects) more efficiently by implementing a new constructor with two parameters, which can be called e.g. with the statement
$teachers[1] = new Teacher('Jan Janssen', 'M');
php
public function __construct(string $name, string $gender)
{
$this->name = $name;
$this->gender = $gender;
}
public function __construct(string $name, string $gender)
{
$this->name = $name;
$this->gender = $gender;
}
1
2
3
4
5
2
3
4
5
- Unfortunately, (constructor) overloading is unsupported in PHP, which means that you can't use the statements (and constructors)
new Teacher('Jan Janssen','M')
andnew Teacher()
interchangeably. Per class, only one constructor can be defined and used! - In this example, we also used a more complex
namespace
(and correspondinginclude_once
anduse
statements) with an additional folder level
Method chaining: class Number
- Open classes/Number.php
php
<?php
namespace classes;
class Number
{
private $x;
public function __construct(float|int $x)
{
$this->x = $x;
}
public function add(float|int $y): Number
{
$this->x += $y;
return $this; // make method chainable
}
public function multiply(float|int $y): Number
{
$this->x *= $y;
return $this; // make method chainable
}
public function printInfo(): Number
{
echo "<p>\$x = $this->x</p>\n";
return $this; // make method chainable
}
}
<?php
namespace classes;
class Number
{
private $x;
public function __construct(float|int $x)
{
$this->x = $x;
}
public function add(float|int $y): Number
{
$this->x += $y;
return $this; // make method chainable
}
public function multiply(float|int $y): Number
{
$this->x *= $y;
return $this; // make method chainable
}
public function printInfo(): Number
{
echo "<p>\$x = $this->x</p>\n";
return $this; // make method chainable
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- Open course/classes_number.php
php
<article>
<?php
include_once '../classes/Number.php'; // include the class
use classes\Number; // use the namespace
echo "<h2>Execute the statements <code>\$number = new Number(10);</code> and <code>\$number->printInfo();</code></h2>\n";
$number = new Number(10);
$number->printInfo();
echo "<h2>Execute the statement <code>\$number->add(90)->multiply(3)->printInfo();</code></h2>\n";
$number->add(90)->multiply(3)->printInfo();
echo "<h2>Execute the statement <code>\$number->printInfo()->multiply(0.1)->printInfo()->add(5)->printInfo();</code></h2>\n";
$number->printInfo()->multiply(0.1)->printInfo()->add(5)->printInfo();
?>
</article>
<article>
<?php
include_once '../classes/Number.php'; // include the class
use classes\Number; // use the namespace
echo "<h2>Execute the statements <code>\$number = new Number(10);</code> and <code>\$number->printInfo();</code></h2>\n";
$number = new Number(10);
$number->printInfo();
echo "<h2>Execute the statement <code>\$number->add(90)->multiply(3)->printInfo();</code></h2>\n";
$number->add(90)->multiply(3)->printInfo();
echo "<h2>Execute the statement <code>\$number->printInfo()->multiply(0.1)->printInfo()->add(5)->printInfo();</code></h2>\n";
$number->printInfo()->multiply(0.1)->printInfo()->add(5)->printInfo();
?>
</article>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- In the (simple) class
Number
, we wrote (besides a constructor) three methods (add()
,multiply()
andprintInfo()
) that all return$this
(the current object). By doing so, we can apply method chaining as in$number->add(90)->multiply(3)->printInfo();
.$number->add(90)
adds 90 to the current number and returns the modified object (which is still$number
).->multiply(3)
multiplies the current number by 3 and returns the modified object again (still$number
).->printInfo()
prints information about the current number, and the object remains the same ($number
).
So, in one line, you add 90, multiply by 3, and then print the information about the modified number, all on the same object $number
. This is the essence of method chaining.
Static methods/properties: class Drawer
- Open classes/Drawer.php
php
<?php
namespace classes;
class Drawer
{
// default values
const PI = 3.14;
private static $type = 'circle';
private static $diameter = 100;
private static $color = 'purple';
public static function circle(float|int|null $diameter = null, string|null $color = null)
{
self::$type = 'circle';
// Default values for circle
self::$diameter = $diameter ?? 100;
self::$color = $color ?? 'blue';
return __CLASS__; // make static method chainable
}
public static function square(float|int|null $diameter = null, string|null $color = null): string
{
self::$type = 'square';
// Default values for square
self::$diameter = $diameter ?? 100;
self::$color = $color ?? 'green';
return __CLASS__; // make static method chainable
}
public static function draw(): string
{
$width = self::$diameter;
$color = self::$color;
if (self::$type == 'circle') {
$radius = self::$diameter / 2;
echo <<<CIRCLE
<p>
<svg height="$width" width="$width">
<circle cx="$radius" cy="$radius" r="$radius" fill="$color" />
</svg>
</p>
CIRCLE;
} else {
echo <<<RECT
<p>
<svg height="$width" width="$width">
<rect x="0" y="0" width="$width" height="$width" fill="$color" />
</svg>
</p>
RECT;
}
return __CLASS__; // make static method chainable
}
public static function printInfo(): string
{
echo "<p><b>" . self::$color . " " . self::$type . "</b> with diameter <b>" . self::$diameter .
" px</b> and surface <b>" . self::surface() . " px²</b></p>\n";
return __CLASS__; // make static method chainable
}
public static function surface(): string
{
if (self::$type == 'circle') {
$surface = (self::PI * self::$diameter ** 2) / 4;
} else {
$surface = self::$diameter ** 2;
}
return $surface;
}
}
<?php
namespace classes;
class Drawer
{
// default values
const PI = 3.14;
private static $type = 'circle';
private static $diameter = 100;
private static $color = 'purple';
public static function circle(float|int|null $diameter = null, string|null $color = null)
{
self::$type = 'circle';
// Default values for circle
self::$diameter = $diameter ?? 100;
self::$color = $color ?? 'blue';
return __CLASS__; // make static method chainable
}
public static function square(float|int|null $diameter = null, string|null $color = null): string
{
self::$type = 'square';
// Default values for square
self::$diameter = $diameter ?? 100;
self::$color = $color ?? 'green';
return __CLASS__; // make static method chainable
}
public static function draw(): string
{
$width = self::$diameter;
$color = self::$color;
if (self::$type == 'circle') {
$radius = self::$diameter / 2;
echo <<<CIRCLE
<p>
<svg height="$width" width="$width">
<circle cx="$radius" cy="$radius" r="$radius" fill="$color" />
</svg>
</p>
CIRCLE;
} else {
echo <<<RECT
<p>
<svg height="$width" width="$width">
<rect x="0" y="0" width="$width" height="$width" fill="$color" />
</svg>
</p>
RECT;
}
return __CLASS__; // make static method chainable
}
public static function printInfo(): string
{
echo "<p><b>" . self::$color . " " . self::$type . "</b> with diameter <b>" . self::$diameter .
" px</b> and surface <b>" . self::surface() . " px²</b></p>\n";
return __CLASS__; // make static method chainable
}
public static function surface(): string
{
if (self::$type == 'circle') {
$surface = (self::PI * self::$diameter ** 2) / 4;
} else {
$surface = self::$diameter ** 2;
}
return $surface;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
- In this last example, we wrote a class
Drawer
to include colored circles and squares (with additional info) on a web page - We don't want to make new objects of this class every time we want to draw something, so we resort to the (somewhat complex) concept of
static
methods/properties- Static properties and methods are accessible without needing an object of the class
- A property declared as
static
is shared among all objects of that class - A method declared as
static
can only access static properties
- A property declared as
- Static methods and properties (and class constants) are accessed using the class name, followed by the scope resolution operator
::
- Static properties and methods are accessible without needing an object of the class
- The static properties
$type
,$diameter
and$color
all get a default value - We can change these default property values by calling one of the static methods
circle()
orsquare()
. The parameters of both these methods have a default value (null
) when they are omitted. - The keyword
self
is used to refer to the current class. It is never preceded by the dollar sign$
and always followed by the::
operator. - The static method
draw()
draws the shape, while the static methodprintInfo()
prints out some info (color, type, size and surface of the shape). Within the latter method, the static methodsurface()
is called. - The method
surface()
uses the class constantPI
, which can be accessed throughself::PI
NAMING CONVENTIONS
Names of (class) constants are in uppercase, without a preceding $
- Notice that the static methods
circle()
,square()
,draw()
andprintInfo()
all end with the statementreturn __CLASS__;
, returning the name of theDrawer
class. As such, we can use static method chaining as inDrawer::circle(150, 'lime')::draw()::printInfo();
- At the end of the execution of
Drawer::circle(150, 'lime')
, the class name (Drawer
) is returned - We call the static method
draw()
on the returned class name (Drawer
). The last statement of this methoddraw()
again returns the class name (Drawer
). - We call the method
printInfo()
on the returned class name (Drawer
)
- At the end of the execution of
- Open course/classes_drawer.php
php
<main>
<h1>Static methods/properties: class Drawer</h1>
<?php
include_once '../classes/Drawer.php'; // include the class
use classes\Drawer; // use the namespace
?>
<section class="grid grid-cols-2 md:grid-cols-3 gap-4">
<article>
<?php Drawer::draw(); ?>
</article>
<article>
<?php Drawer::circle(150, 'lime')::draw()::printInfo(); ?>
</article>
<article>
<?php Drawer::square(null, 'gold')::printInfo()::draw(); ?>
</article>
<article>
<?php Drawer::square()::draw()::printInfo(); ?>
</article>
<article>
<?php Drawer::circle()::draw(); ?>
</article>
</section>
</main>
<main>
<h1>Static methods/properties: class Drawer</h1>
<?php
include_once '../classes/Drawer.php'; // include the class
use classes\Drawer; // use the namespace
?>
<section class="grid grid-cols-2 md:grid-cols-3 gap-4">
<article>
<?php Drawer::draw(); ?>
</article>
<article>
<?php Drawer::circle(150, 'lime')::draw()::printInfo(); ?>
</article>
<article>
<?php Drawer::square(null, 'gold')::printInfo()::draw(); ?>
</article>
<article>
<?php Drawer::square()::draw()::printInfo(); ?>
</article>
<article>
<?php Drawer::circle()::draw(); ?>
</article>
</section>
</main>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
REMARK
- In PHP, built-in classes like
Math
,PDO
, andDateTime
use static methods and properties for common tasks, offering utility and organization. - When creating your own classes, consider using static methods for utility functions and static properties for shared data management.