Have you needed any vendor files (third party libs) that are already written in PHP5.3 and use namespaces?
You might find out that they usually don’t work out of the box.
The included class is not the problem, but all referenced classes inside this one and its namespace.
I also could not get it to work right away. With some help from tigrang at stackoverflow (namespaces-in-vendor-files-cakephp2-x-and-php5-4-3), though, I could manage to get the class running.
For future reference and for those who stumble upon the same issue here a quick guide.
The namespace loader
Since Cake2.x does not support namespaces yet (3.x then will) we have to write our own loader. You can either put it into your bootstrap or above the class which uses the namespaced vendor lib. I went with the second for now, but the first one would be a more long-term solution.
// PHP5.3 namespace loader for Cake2.x
spl_autoload_register(function ($class) {
foreach (App::path('Vendor') as $base) {
$path = $base . str_replace('\\', DS, $class) . '.php';
if (file_exists($path)) {
include $path;
return;
}
}
});
That’s all you need. Now you can import your vendor file as usual:
// APP/Lib/MyClass.php
App::import('Vendor', 'PackageName', array('file' => 'PackageName/SubFolder/ClassName.php'));
class MyClass {
protected $_Engine;
public function __construct($settings) {
parent::__construct($settings);
if (!class_exists('PackageName\SubFolder\ClassName')) {
throw new RuntimeException('Your desired vendor library cannot be found');
}
$this->_Engine = new PackageName\SubFolder\ClassName($settings);
}
}
Using classes from the vendor namespace
At some point you might want to use more of the classes in the vendor namespace directly in your cake lib class. You cannot just use say the class "Rule" from "PackageName\SubFolder\SubSubFolder". You need to prepend the namespace to it:
$newRule = new PackageName\SubFolder\SubSubFolder\Rule($argument);
Same goes for if (class_exists()) {}
or similar checks like:
if ($value instanceof PackageName\SubFolder\SubSubFolder\Value\URL) {}
Note: this article is PHP5.3 and above! You can also use this in your 2.x app if you have this minimal php version running (which shouldn’t be a problem nowadays – and with PHP5.4 upcoming already).
It shows that it is possible to use namespaced third party products in your Cake2.x apps.
You should add a return after the include in the autoload function
Good point. Corrected it 🙂
Thanks, Mark, but I have to tell you, that this is not working with vendor libraries in plugins.
Or maybe I am doing it wrong?
I am having the Paymill library in a plugin called "Payment" in (from app root): "Plugin/Payment/Vendor/Paymill"
I am trying import the vendor library like this:
I put the autoloader in the bootstrap.php of the Payment plugin.
Now I get this:
Error: Class ‘Paymill\API\Curl’ not found
This is because App::path(‘vendor’) does not include the right paths for the autoloading.
It tries only these two in the autoloader:
app/Vendor/Paymill/API/Curl.php
cakephp2.3/vendors/Paymill/API/Curl.php
Am I doing something wrong here?
Regards
func0der
Pretty dirty way, but I put this in the Payment plugins bootstrap.php:
Any better solutions?
Hi Mark, I can’t make it work. Can you please help me out?
I’m using this lib from https://github.com/jwage/purl.
On the bottom of my bootstrap.php I put this code
I imported the main class in the top of my controller
and us it in an action
$url = new Purl\Url($this->request->data[‘Post’][‘url’]);
debug($url->registerableDomain);
and get this error
Fatal Error
Error: Class ‘Purl\AbstractPart’ not found
File: C:\xampp\htdocs\mimosis\app\Vendor\purl\src\Purl\Url.php
Line: 37
Do you know what am i missing, Mark? Thank you.
This is my code in bootstrap.php
And in my Component:
use class in function:
And that just worked for me!