The problem: data structures and methods that can work on them.
In your case you have two values which appear to belong together, a quantity and a price. You could always keep them around in two separate variables:
$qty = 20;
$price = 40;
And then all functions that use that data accept two arguments:
function income($qty, $price) { ... }
What about data structures that require a bit more than two fields, say a product description; are you going to keep all those as individual variables?
$product_name = 'Foo';
$product_price = 400;
$product_description = 'Lorem ipsum';
...
There are typically dozens of properties a product has; will you keep an individual variable for each, and have each function accept dozens of arguments?
function display_product($product_name, $product_price, $product_description, ...) { ... }
That's pretty impractical.
So you define an actual data structure that you can carry around in a single variable:
$product = [
'name' => 'Foo',
'price' => 400,
...
];
Now each function that works with product data only needs to accept a single argument which contains all the data:
function tax(array $product) {
return $product['price'] * 0.08;
}
But you still have disparate data definitions; your $product
array structure is defined over here, but there may be any number of functions defined over there, and if you change something about one or the other they might stop working together correctly. So, let's bundle them up into one thing!
class Product {
public $name;
public $price;
public $description;
...
public function tax() {
return $this->price * 0.08;
}
}
The tax
method works on specific product data, and it's bundled together with the actual data it's working on, so there's little chance of these two pieces diverging.
That's the basis of OOP and the use of properties. More interesting use cases can be build on that; e.g. polymorphism, which isn't really practical using only functions and variables.