Dividing and Conquering through Namespace Architecture
Defining namespaces is pretty simple. At the top of the file, right after the opening PHP tag, the namespace keyword is used, followed by the virtual directory of your choosing. This virtual directory can act as a single directory, or be few sub-directories deep. We’re also going to be looking at using the use keyword. Remember the use keyword? Yeah, well it’s coming back.
Recap: Review the previous article and code. We’ll be building off of our previous code.
https://github.com/dinocajic/php-youtube-tutorials/tree/master
https://blog.devgenius.io/php-p67-parent-constructor-ec8e41b9a6a9
Let’s start off by creating our test file.
<?php
// Defining Namespaces
require_once("Lamborghini.php");
$lamborghini = new Lamborghini(1999, "Lamborghini", "Murcielago", "1234");
echo $lamborghini->get_year_make_and_model();
The code prints out 1999 Lamborghini Murcielago.
Up till now, we have not defined any namespaces. That will change shortly. If you open any of the code that we currently have, you’ll notice that there is no namespace keyword anywhere, which means that namespaces are not defined. If they did, the namespace keyword would be located right after the opening <?php tag.
Defining namespaces is pretty simple. Let’s make something up for our test code above. I’m going to define the CoolCars namespace.
<?php
// Defining Namespaces
namespace CoolCars;
require_once("Lamborghini.php");
$lamborghini = new Lamborghini(1999, "Lamborghini", "Murcielago", "1234");
echo $lamborghini->get_year_make_and_model();
It’s really that simple…and we’ve broken the code. Our editor automatically acts up and says that the Lamborghini class is undefined.
And sure enough, if we try to run the code we get an error. What happened though? Well we defined the CoolCars namespaces and the code inside of that file belongs to that namespace (the virtual directory).
All of our other files that do not have a namespace defined are technically located in the global space. It’s almost as if we created a subdirectory called CoolCars that contains our test file and everything else is outside of that directory.
When we’re trying to require_once(“Lamborghini”), PHP is looking inside of the CoolCars virtual directory. It can’t find it inside there since Lamborghini is currently located within the global namespace.
What’s a quick way to fix this? Just add a backslash to the Lamborghini class. It tells PHP to look in the global directory.
<?php
// Defining Namespaces
namespace CoolCars;
require_once("Lamborghini.php");
$lamborghini = new \Lamborghini(1999, "Lamborghini", "Murcielago", "1234");
echo $lamborghini->get_year_make_and_model();
Testing our code yields the result that we were looking for: 1999 Lamborghini Murcielago. Seems a hackish way to do it. Is there another way? Of course there is. Let’s add Lamborghini to the CoolCars namespace.
<?php
namespace CoolCars;
require_once("Car.php");
class Lamborghini extends Car {
private string $secret_code;
public function __construct( $year, $make, $model, $secret_code ) {
parent::__construct($year, $make, $model);
$this->secret_code = $secret_code;
}
/**
* @return string
*/
public function getSecretCode(): string
{
return $this->secret_code;
}
/**
* @param string $secret_code
*/
public function setSecretCode(string $secret_code): void
{
$this->secret_code = $secret_code;
}
}
And we broke the code again. I think you know what happens next. First, we could append the \ in front of the Car where it’s getting extended.
class Lamborghini extends \Car
Or, we could add Car to the CoolCars namespace.
?php
namespace CoolCars;
require_once('Vehicle.php');
require_once('Engine.php');
require_once('Transmission.php');
class Car extends Vehicle
{
use Transmission, Engine {
Transmission::check_oil_level insteadof Engine;
Engine::check_oil_level as check_engine_oil_level;
}
const HAS_HEADLIGHTS = true;
const HAS_TAIL_LIGHTS = true;
const HAS_TURN_SIGNALS = true;
//...
}
We’re not done yet. The Car class extends the Vehicle class and uses the Transmission and Engine traits. They all have to be added to the same namespace.
<?php
namespace CoolCars;
class Vehicle {
protected string $color;
protected string $make;
//...
}
<?php
namespace CoolCars;
trait Engine {
public function check_oil_level() {
echo "Engine oil level good";
}
}
<?php
namespace CoolCars;
trait Transmission {
public function check_oil_level() {
echo "Transmission oil level good";
}
}
Now that everything is living within the same namespace, if we refresh our code, PHP doesn’t complain and the desired output is displayed.
What if we removed the CoolCars namespace from the test code? Our structure would now look like this.
The Lamborghini class is not in the same namespace as our test code. The quick fix: add the CoolCars namespace in front of the Lamborghini class during instantiation.
<?php
// Defining Namespaces
require_once("Lamborghini.php");
$lamborghini = new CoolCars\Lamborghini(1999, "Lamborghini", "Murcielago", "1234");
echo $lamborghini->get_year_make_and_model();
It just says that this file will be using the Lamborghini class from the CoolCars namespace. This has replaced our Namespace\Class call during instantiation. We can now use just our Lamborghini class like we did before.
This is a good stopping point. In the next article we’ll look at sub-namespaces.
JUST A VIRTUAL DIRECTORY TRICK
Namespaces are used throughout numerous programming languages. If you’re familiar with Java, you might have heard of the term package. Similar but different.
Dividing and Conquering through Namespace Architecture
Defining namespaces is pretty simple. At the top of the file, right after the opening PHP tag, the namespace keyword is used, followed by the virtual directory of your choosing.
CULTIVATING MINIATURE WORLDS THROUGH SUB-NAMESPACE
If you can define namespaces, then it’s no different to define sub-namespaces. We’re also going to look at classes with the same name from different sub-namespaces and how we’re going to handle those naming conflicts that still might arise.