My First PHP OOP's Programme or Project - Calculate: add, subtract or Multiply

Index.php →

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<form action="calc.php" method="post">
			<input type="text" name="num1" value="">
			<input type="text" name="num2" value="">
			<select name="cal">
				<option value="add">Add</option>
				<option value="sub">Subtract</option>
				<option value="mul">Multiply</option>
			</select>
			<button type="submit">Calculate</button>
		</form>
	</body>
</html>

calc.php →

<?php
include 'includes/calc.inc.php';

$num1 =  $_POST['num1'];
$num2 =  $_POST['num2'];
$cal =  $_POST['cal'];

$calculator = new Calc($num1, $num2, $cal);
echo $calculator->calcMethod();

calc.inc.php →

class Calc {
	public $num1;
	public $num2;
	public $cal;

	public function __construct($num1, $num2, $cal) {
		$this->num1 = $num1;
		$this->num2 = $num2;
		$this->cal = $cal;
	}

	public function calcMethod() {
		switch ($this->cal) {
			case 'add':
				$result = $this->num1 + $this->num2;
				break;
			case 'sub':
				$result = $this->num1 - $this->num2;
				break;
			case 'mul':
				$result = $this->num1 * $this->num2;
				break;

			default:
				$result = "Error";
				# code...
				break;
		}
		return $result;
	}
}

I have a couple of questions to understand OOP’s better through this solid example.

This is the snippet from calc.inc.php →

	public $num1;
	public $num2;
	public $cal;

	public function __construct($num1, $num2, $cal) {
		$this->num1 = $num1;
		$this->num2 = $num2;
		$this->cal = $cal;
	}

This is the snippet from calc.php →

$num1 =  $_POST['num1'];
$num2 =  $_POST['num2'];
$cal =  $_POST['cal'];

$num1, for example is same in both the calc.php and calc.inc.php or these would have been a different named variable still the programme would have been working?

You are asking about variable scoping;

To summarize, there is no connection between your variables even if they share the same name. Variables declared inside of a class are only relevant within that class.

There are things known as globals which can be accessed everywhere. But that is a slightly different topic. Also discussed in the above link.

1 Like

I would not pass all those values to the constructor and then call a method on the class to use the values. The problem here is that you have what’s called temporal coupling, meaning the result of calcMethod is dependent on something that happened before (calling the constructor in this case). So if somewhere else one of the values is changed then the result it not what you would expect.

Instead I would say that the operations are stateless, you don’t need to store anything in the class in order to do those calculations.

Instead I would propose:

class Calculator
{
    public function calculate($operator, $value1, $value2)
    {
        switch ($operator) {
            case 'sum':
                return $value1 + $value2;
            case 'sub':
                return $value1 - $value2;
            case 'mul':
                return $value1 * $value2;
        }

        throw new InvalidArgumentException(sprintf('Unknown operator "%s"', $operator));
    }
}

which you would then call as follows:

$calculator = new Calculator();
$calculator->calculate($_POST['cal'], $_POST['num1'], $_POST['num2']);

The only problem is that this class does not adhere to the open/closed principle, as you would have to edit it every time you want a new operator. If you wanted a class that adheres to open/closed fully you should do something like this:

class Calculator
{
    private $operators;
    
    public function calculate($operator, $value1, $value2)
    {
        if (!array_key_exists($operator, $this->operators)) {
            throw new InvalidArgumentException(sprintf('Unknown operator "%s"', $operator));
        }

        return $this->operators[$operator]->calculate($value1, $value2);
    }

    public function addOperator($operator, CalulatorOperation $operation)
    {
        $this->operators[$operator] = $operation;
    }
}

interface CalculatorOperation
{
    public function calculate($value1, $value2);
}

public function SumOperation implements CalculatorOperation
{
    public function calculate($value1, $value2)
    {
        return $value1 + $value2;
    }
}

public function SubOperation implements CalculatorOperation
{
    public function calculate($value1, $value2)
    {
        return $value1 - $value2;
    }
}

public function MulOperation implements CalculatorOperation
{
    public function calculate($value1, $value2)
    {
        return $value1 * $value2;
    }
}

Which you would then call as follows:

$calculator = new Calculator();
$calculator->addOperator('sum', new SumOperation());
$calculator->addOperator('sub', new SubOperation());
$calculator->addOperator('mul', new MulOperation());

$calculator->calculate($_POST['cal'], $_POST['num1'], $_POST['num2']);

That seems like a lot of work for a calculator, but it’s completely immutable - it has no side effects or temporal coupling whatsoever, it’s testable, and it’s very easily extendable for new operations.

3 Likes

So, If I do this still the code will remain relevant and functional →

$random_num1 =  $_POST['num1'];
$random_num2 =  $_POST['num2'];
$random_cal =  $_POST['cal'];


$calculator = new Calc($random_num1, $random_num2, $random_cal);
echo $calculator->calcMethod();

Right?

I didn’t understand this part sir. Can you please explain with a short example?

Yep. The names are not important. No link between the two. Heck, you could even do this:

class Calc {
    private $num1;
    private $num2;
    private $cal;

    public function __construct($arg1, $arg2, $arg3) {
        $this->num1 = $arg1;
        $this->num2 = $arg2;
        $this->cal = $arg3;
    }

Not recommended but it would work.

And please stop using tabs. Have your IDE expand tabs into spaces.

1 Like

5 posts were merged into an existing topic: Tabs vs. Spaces

Well, suppose I use the class from your first post and use it like this:

$calculator = new Calculator(4, 5, 'sum');
$calculator->num1 = 5;
$sum = $calculator->calcMethod();

The result would be 10. Now in this small example it’s quite easy to follow, but suppose multiple different classes are sharing the calculator. It will become an undebuggable mess quite quickly.

2 Likes

Just like I shared one simple example above. From where can I get such short PHP OOP’s project to practice?

I just wanted to say that @rpkamp 's second example code is an excellent example of OOP done right and deserves more recognition than simply clicking the love button under the post!

Though you may want to change the addOperator method so that the calculator class is also immutable.

2 Likes

I know that and I didnrt disregarded him, but it is too early in the day for novice like me to grasp so much. I have bookmarked that fro future analysis may be 2 months from now.

It is a common request and I really wish give you a couple of links but that is a lot harder to do then you might think.

But since you are hanging out around here there is a book called something like “From PHP Novice to Ninja” for which you will see a number of threads. It’s not a bad place to get started and there are several people around here that can help.

I also think https://symfony.com/doc/current/introduction/from_flat_php_to_symfony2.html is a pretty good resource though it might be just a little bit advanced (object wise) for you.

2 Likes

Yes, It is. I tried.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.