This is the second part of posts about transitioning from procedural to object oriented programming paradigm.
In this post we will take the basic calculator script that we wrote in a procedural way in the previous post and we will transition it to object oriented programming paradigm. You can find this script at my github repository.
Improve code quality and readability by transforming our procedural code to object oriented code. The script's functionality will stay the same it will still be a CLI Calculator script which handles adding, subtracting, multiplying and division.
Create a php file named: object-oriented-calculator.php
. With the following contents:
<?php declare(strict_types=1);
if (php_sapi_name() !== 'cli') {
die('This script can be called only from the command line.' . PHP_EOL);
}
This step is basically the same as the one in the previous post, the difference is that we are not handling count of the provided arguments because we will do that in the constructor. Check for the CLI Interface stays the same because we want to ensure that nobody can call it from a web server.
Use of php_sapi_name()
function is explained in the previous post.
Skeleton of our class:
class Calculator
{
/**
* @var string
*/
private $rawArgument;
/**
* @var int
*/
private $firstNum;
/**
* @var int
*/
private $secondNum;
/**
* @var string
*/
private $operator;
/**
* @var array
*/
private $allowedOperators = [
'+',
'-',
'*',
'/'
];
public function __construct(array $argv)
{
if (count($argv) <= 1) {
throw new InvalidArgumentException('You need to provide an operation!');
}
$this->input = $argv[1];
}
}
We have defined a class named Calculator
.
This class also holds properties such as $rawArgument
or $allowedOperators
.
These properties can be only used within this class, because they are defined as private
visibility in PHP.
There are three types of properties visibility within a class. First we have public
visibility which can be used by any class, then we have
protected
visibility which can be only used in classes that extends this class and lastly a private
visibility which can be only used within the
class that you defined these properties.
At the end we defined a method
(which is still named function
, but in Object oriented programming
it is called a method
)
which is called __construct.
This is a special method
in PHP which get's called automatically when we create an object (eg.: new Calculator($argv)
).
We accept one argument of type array
and we immediately check if this array has one or less element in it, if it does we throw an
exception because it did not satisfy our requirement. If this array has more than
one element we assign it to our $this->input
property which we can use elsewhere in our class
.
Methods starting with two underscores __construct
, __invoke
, etc. are called magic methods.
private function add(int $a, int $b): int
{
return $a + $b;
}
private function subtract(int $a, int $b): int
{
return $a - $b;
}
private function multiply(int $a, int $b): int
{
return $a * $b;
}
private function divide(int $a, int $b): float
{
return $a / $b;
}
The difference is very subtle we only added visibility
to our methods if we compare it to the procedural
way.
We defined three methods, and they will use the fluent interface where we chain methods sequentially.
private function findOperator(): self
{
foreach ($this->allowedOperators as $operator) {
if (strpos($this->input, $operator)) {
$this->operator = $operator;
return $this;
}
}
throw new InvalidArgumentException('Unrecognized operand provided!');
}
private function parseInput(): self
{
[$this->firstNum, $this->secondNum] = explode($this->operator, $this->input);
if (is_numeric($this->firstNum) && is_numeric($this->secondNum)) {
return $this;
}
throw new InvalidArgumentException('Provided arguments are not of type integer.');
}
private function getResult(): float
{
switch ($this->operator) {
case '+':
$result = $this->add((int)$this->firstNum, (int)$this->secondNum);
break;
case '-':
$result = $this->subtract((int)$this->firstNum, (int)$this->secondNum);
break;
case '*':
$result = $this->multiply((int)$this->firstNum, (int)$this->secondNum);
break;
case '/':
$result = $this->divide((int)$this->firstNum, (int)$this->secondNum);
break;
}
return $result;
}
Method findOperator
iterates over the $this->allowedOperators
array
and checks if the input provided by the user is valid.
If the user provides a valid operation this method will return itself back otherwise it will throw an exception
. Next method
parseInput
seperates the user input into $this->firstNum
and $this->secondNum
and returns itself back otherwise it throws an exception
if these properties are not numerical. The getResult
method just switches between
operations and calls the correct method
to provide the result.
We can use the fluent interface
now and wrap it in a calculate
method:
public function calculate(): float
{
return $this->findOperator()
->parseInput()
->getResult();
}
The only thing that is now left is constructing our Calculator
class and calling the calculate
method.
Define this part outside of the class Calculator
block:
try {
$calculator = new Calculator($argv);
printf('Result: %s' . PHP_EOL, $calculator->calculate());
} catch (Throwable $e) {
printf('Error: %s' . PHP_EOL, $e->getMessage());
}
We provide $argv
as our argument to the __construct
magic method and then call the `$calculator->calculate()
method.
If the provided input was correct we return a result otherwise we catch
an exception
that we threw and display it's message.
Call this script like so: php object-oriented-calculator.php 10/5
. The same goes for the other operations.
With this post we have finished transitioning our code from procedural
paradigm to object oriented
paradigm.
This was an introduction to the object oriented paradigm in future posts we will take a deeper look at object oriented programming
.