I have a file (an xmlrpc library for PHP) that I would like to use into a Symfony2 class (so inside a Symfony2 project).
I can't use autoloader because as written here
which is able [the autoloader] to load classes from files that
implement one of the following conventions:
1) The technical interoperability standards for PHP 5.3 namespaces and
class names;
2) The PEAR naming convention for classes.
If your classes and the third-party libraries you use for your project
follow these standards, the Symfony2 autoloader is the only autoloader
you will ever need.
the class that I'll go to use, didn't satisfies one of those requirements.
So if I can't autoload that file, since isn't possible (as I understand, but I can go wrong) to use require_once (or simply require) with namespace, what is the solution for this issue?
Assuming you have a file named xmlrpc.lib.php with the following content:
<?php
class XMLRPC_Server {
// ...
}
class XMLRPC_Request {
// ...
}
class XMLRPC_Response {
// ...
}
You can create an instance of MapClassLoader to handle it's autoloading:
// Create map autoloader
$mapLoader = new MapClassLoader(array(
'XMLRPC_Server' => __DIR__.'/../library/xmlrpc.lib.php',
'XMLRPC_Request' => __DIR__.'/../library/xmlrpc.lib.php',
'XMLRPC_Response' => __DIR__.'/../library/xmlrpc.lib.php',
));
// Register autoloader
$mapLoader->register();
When one of these classes will be auto-loaded, others will be auto-loaded too because they share the same PHP file.
Related
I want to ask some questions about the Zend framework. if someone knows please answer it will be a great help.
Q1. Can we create classes, abstract and derived classes in any folder
of the Zend Project?
Q2. If Q1 answer is yes how we can database access (from the model
class) or we need to utilize any other Zend related functionality.
Q3. Is it possible to place abstract class and the implementation
of an class within a dedicated folder somewhere within the source
tree?
Q1 - Can we create classes, abstract and derived classes in any folder of the Zend Project?
Yes, you can, but it is not encouraged. Why? Because it would end up in a messy project, with a portion of code in a given location, other code somewhere else, other snippets in a third location.. This will make maintenance more complex than it should be, wasting time searching for the code between all different locations.
For this subject, I suggest you to read the PSR-4 specifications and directives
That being said, let's make this example. You want to create a class OutsideClass, with namespace OutsideCode, in folder outsideFolder.
What you need to do is:
Create in the folder outsideFilder, which will be located in the root folder of your project.
Create the class OutsideClass:
<?php
namespace OutsideCode;
class OutsideClass
{
// ...
// Class properties, constructor and methods
// ...
}
Add the namespace inside the composer.json, so the application will be able to translate a namespace into a phisical location:
"autoload": {
"psr-4": {
...
"OutsideCode\\": "outsideFolder/"
}
}
Run composer dump-autoload to recreate autoload file
End. You can now put all the code you want inside this folder, always respecting PSR-4 directives
Use your new classes from your controller/mappers/forms/validators/...
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use OutsideCode\OutsideClass;
class IndexController extends AbstractActionController
{
public function indexAction()
{
$outsideClass = new OutsideClass();
}
}
Q2 - If Q1 answer is yes how we can database access (from the model class) or we need to utilize any other Zend related functionality.
It is not mandatory to use Zend related functionalities.
Nobody prevents you to access your write all your code from scratch, using only PHP methods, to access the database, or to validate a phone number, or to send an email. If you want to use Zend functionalities (or classes that you already wrote), just import them with a use statement and the top of the file (as shown in the previous snippet).
The most important thing is always: DRY and don't reinvent the wheel (+this)
Q3 - Is it possible to place abstract class and the implementation of an class within a dedicated folder somewhere within the source tree?
Yes, until its position follows PSR-4 specifications and directives, otherwise it won't be loaded (nor executed)
Here is the scenario.
I am implementing namespaces into my projects.
I have my own custom bridge library that calls other libraries like Zend to do the heavy lifting.
I have no problem using fully qualified namespaces in my custom bridge library but would like to keep the code as terse as possible in my controllers, models and view.
Here is an example of some aliasses i would like to use:
use BridgeLibName\Stdlib\Arrays as arr;
use BridgeLibName\Stdlib\Objects as obj;
use BridgeLibName\Stdlib\Strings as str;
use BridgeLibName\Stdlib\Numbers as num;
use BridgeLibName\Stdlib\File as file;
etc.........
Example usage:
$file = new file('path/to/file.txt');
$file->create();
or
$obj = arr::toObject(['key1'=>'value1']);
is it possible in any way to create an alias or constant that can be globally accessible and not discarded at the end of each file?
Some kind of bootstrap file that can make these aliases stick.
As I was writing the question i thought of a solution.
You can fake it by creating classes that extend the namespaced classes.
example:
class arr extends BridgeLibName\Stdlib\Arrays{
}
One important thing to remember:
If you are going to extend the classes the namespaced class will have to be loaded.
This could have performance implications if used too much since aliases and namespaces are only loaded as needed.
Since I am only using it to bridge to other classes there is very little logic inside my bridge files.
These bridge files in turn uses aliases and namespaces correctly thus loading the real files as needed.
I you are not careful with the implementation you can load a lot of unnecessary stuff and cause your app to become slow and bloated.
A nice thing I noticed is that good IDEs like netbeans also seems to be able to do auto completion with this method.
If there is a better way to do this please let me know.
Just thought of an amendment to this method to fix the problem with unnecessary class instantiation.
The core library can work with the normal psr-0 loader.
To have the aliases autoload I created an aditional dir named includes next to my namespaced class.
in composer you describe it like so:
"autoload": {
"psr-0": {
"BridgeLibName\\": "."
},
"classmap": ["include/"]
}
Now your libraries will load as expected from the correct namespace and your alias classes will autoload as needed.
Classes put into the include dir can now extend the namespaced classes ( as shown above ) and will no longer be loaded prior to being used.
Now you have global aliases without having to sacrifice performance by loading unused classes.
I have a site which uses the library file lib.client.php which is stored in the php folder in my standard website root and contains a series of classes I have built.
The library file contains about 5 or so classes, should I leave this file as one or break up the classes into their own files and include them all individually? Are there any best practice naming conventions I should use for these file(s)?
(As you can see at the moment I'm using lib. and I also use inc. - only because I have seen it done a few times before).
UPDATE:
I am remodelling my structure to comply with the PSR-0 Standard. I now have:
CCall (Vendor)
Core
Connection
Gateway.php
GatewayDSN.php
GatewayException.php
Components
Environment.php
EnvironmentRequest.php
Centre.php
Access
User.php
UserSession.php
RenderException.php
I want to create a new Environment() in index.php, and its __construct method calls Gateway::checkInstance().
How would I manage namespace use in this model? What would have its own namespace and where would I define this?
How would I use an autoload with these namespace definitions (and where?)
Is there an equivalent standard for method and property naming?
I am using this https://gist.github.com/jwage/221634/download#
Break classes into their own files and follow the PSR-0 standard as a best practice.
http://phpmaster.com/autoloading-and-the-psr-0-standard/
If you are using a PSR-0 autoload:
add this in Environment.php
namespace Components;
and add a reference to Gateway
use Core\Connection\Gateway;
of course you need this line inside Gateway.php
namespace Core\Connection;
Then:
new Components\Environment();
I've been playing a bit with the Symfony class loader (read about others, the concept and started implementing).
I've read http://symfony.com/doc/current/components/class_loader.html as well
I've changed the directories structure to fit and all. Here's a small source-code which fails:
Filename: test.php , dir: C:/test/BE/src/main.php
<?php
define('BASE_DIR','/../..');
echo BASE_DIR.'/BE/libs/symphony/component/ClassLoader/2.1.0/UniversalClassLoader.php';
require_once BASE_DIR.'/BE/libs/symphony/component/ClassLoader/2.1.0/UniversalClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->registerNamespace('App\Location\Location', 'Location/Location');
// You can search the include_path as a last resort.
$loader->useIncludePath(true);
$loader->register();
use App\Location\Location;
new App\Location\Location(); //Fatal error: Class 'App\Location\Location' not found in C:/test/BE/src/main.php
file name: Location.php , dir: C:/test/BE/src/Location/Location.php
namespace App\Location;
class Location{
private $lat;
private $lng;
}
By registering your namespace as follows:
$loader->registerNamespace('App\Location\Location', 'Location/Location');
autoloader will look for the App\Location\Location class in the Location/Location/App/Location/Location.php file. Note that file/directory names are case sensitive.
First parameter of registerNamespace() is either a namespace or part of the namespace. Second parameter is a path to a PSR-0 compliant library. That means that directory structure inside that path should follow PSR-0 standard. This path is not used when calculating path for given namespace.
If you want to keep your current directory structure following code should work for you:
$loader->registerNamespace('App', __DIR__);
I'd rather put your source code in one common directory, for example src:
src/
App/
Location/
Location.php
And then register namespaces as follows:
$loader->registerNamespace('App', 'src');
By doing that you're simply telling an autoloader that it should look for App namespaced classes in the src folder. Note that it will work for every class in the App namespace (following PSR-0).
Some time ago I wrote a blog post about the Symfony autoloader. You might find it helpful: Autoloading classes in an any PHP project with Symfony2 ClassLoader component
Since you plan to use Pimple and some of the Symfony components take a look at Silex microframework. Might be it's something you need to implement.
You use:
$loader->registerNamespace('Pimple', BASE_DIR.'/BE/libs/Pimple/1.0.0/lib/Pimple');
To register the Pimple namespace for Pimple is useless, as Pimple is one class in the default namespace (it has no namespace).
The UniversalClassLoader does only work for PSR-0 and PEAR compatible directory layouts. If your layout is not, you should not expect it to work nor to be documented that it works for incompatbile layouts.
Explicit Note: You are using the wrong tool to do the job. All file layouts you have shared over the last hours are incompatible with UniversalClassLoader.
Apart from that if you give the wrong values for it's parameters, the class just won't work.
I am integrating Zend Framework into an existing application. I want to switch the application over to Zend's autoloading mechanism to replace dozens of include() statements.
I have a specific requirement for the autoloading mechanism, though. Allow me to elaborate.
The existing application uses a core library (independent from ZF), for example:
/Core/Library/authentication.php
/Core/Library/translation.php
/Core/Library/messages.php
this core library is to remain untouched at all times and serves a number of applications. The library contains classes like
class ancestor_authentication { ... }
class ancestor_translation { ... }
class ancestor_messages { ... }
in the application, there is also a Library directory:
/App/Library/authentication.php
/App/Library/translation.php
/App/Library/messages.php
these includes extend the ancestor classes and are the ones that actually get instantiated in the application.
class authentication extends ancestor_authentication { }
class translation extends ancestor_translation { }
class messages extends ancestor_messages { }
usually, these class definitions are empty. They simply extend their ancestors and provide the class name to instantiate.
$authentication = new authentication();
The purpose of this solution is to be able to easily customize aspects of the application without having to patch the core libraries.
Now, the autoloader I need would have to be aware of this structure. When an object of the class authentication is requested, the autoloader would have to:
1. load /Core/Library/authentication.php
2. load /App/Library/authentication.php
My current approach would be creating a custom function, and binding that to Zend_Loader_Autoloader for a specific namespace prefix.
Is there already a way to do this in Zend that I am overlooking? The accepted answer in this question kind of implies there is, but that may be just a bad choice of wording.
Are there extensions to the Zend Autoloader that do this?
Can you - I am new to ZF - think of an elegant way, conforming with the spirit of the framework, of extending the Autoloader with this functionality? I'm not necessary looking for a ready-made implementation, some pointers (This should be an extension to the xyz method that you would call like this...) would already be enough.
To extend what Gordon already pointed out, I'd create my own autoloader class that implements Zend_Loader_Autoloader_Interface and push that onto the Zend_Loader_Autoloader-stack.
class My_Autoloader implements Zend_Loader_Autoloader_Interface
{
public function autoload($class)
{
// add your logic to find the required classes in here
}
}
$autoloader = Zend_Loader_Autoloader::getInstance();
// pushAutoloader() or unshiftAutoloader() depending on where
// you'd like to put your autoloader on the stack
// note that we leave the $namespace parameter empty
// as your classes don't share a common namespace
$autoloader->pushAutoloader(new My_Autoloader(), '');
I wouldn't go with the Zend_Loader approach as, even though not deprecated yet, the new Zend_Loader_Autoloader seems to be best practice currently.
See the manual on Zend_Loader:
By default, the autoloader is configured to match the "Zend_" and "ZendX_" namespaces. If you have your own library code that uses your own namespace, you may register it with the autoloader using the registerNamespace() method.
$autoloader->registerNamespace('My_');
Note that the autoloader follows the ZF Naming Convention, so Zend_Foo_Bar will look in Zend/Foo/Bar.php.
However,
You can also register arbitrary autoloader callbacks, optionally with a specific namespace (or group of namespaces). Zend_Loader_Autoloader will attempt to match these first before using its internal autoloading mechanism.
$autoloader->pushAutoloader(array('ezcBase', 'autoload'), 'ezc');
Another way would be to create custom class Loader extending Zend_Loader and then register it with:
Zend_Loader::registerAutoload('My_Loader');
ZF will then use this autoloader instead of the default one.