The application layout with my composer package is as follows
some-application
├── index.php
└── composer.json
└── vendor/my/package
├── composer.json
└── src
├── Foobar
│ └── style.css.php
└── Bar
├── Moo.php
└── Baz.php
index.php uses style.css.php as a stylesheet in its html markup. The stylesheet is a php file because the styles need to get rendered dynamically. So, style.css.php is accessed directly by the clients browser, without passing index.php that includes the autoloader.
Now, I would like to access Moo and Baz in style.css.php, but what is the correct approach to define some kind of autoloader for my package that allows this?
I was only able to find info regarding the autoloader that the application would include. But what if I bypass the application's autoloader invocation?
Any advice is much appreciated, thanks.
To autoload composer package classes, you just need to somehow load the autoloader.
In your script, you can say:
<?php
$path = file_exists('vendor/autoload.php') ? 'vendor/autoload.php' : '../../../autoload.php';
require_once $path;
use Bar\Moo;
$moo = new Moo();
This will load the application's autoloader when the package is included in an application, or will load the package's own autoloader when you develop the package, e.g. running tests.
Related
I'm working in a WordPress plugin, following the WordPress Coding Standards.
This is the current structure for the plugin:
.
├── includes
├── languages
├── views
├── class-plugin-admin.php
├── class-plugin.php
└── plugin.php
That said, I would like to use the Composer autoloader to load both classes (for unit testing purposes).
Today I've been loading them like this:
"autoload-dev": {
"files": [
"class-plugin.php",
"class-plugin-admin.php"
]
}
However, not sure if this one would be the best way to do it.
So here are the questions:
Should I move my classes to a subdirectory in the plugin?
Which one would be the best way to load them via composer?
Should I move my classes to a subdirectory in the plugin?
You don't need to, but you can. It definitely makes developing the plugin a lot easier, and I would suggest to do so.
As you indicated, you probably already have tests, so why not go ahead and adjust the structure to something like this:
.
├── includes
├── languages
├── src
│ ├── Plugin.php
│ └── PluginAdmin.php
├── test
│ ├── PluginAdminTest.php
│ ├── PluginTest.php
│ └── phpunit.xml
├── views
├── composer.json
└── plugin.php
Note I adjusted the class names, because ideally, you probably want to use some way of PSR-4 autoloading for your classes.
For reference, see:
https://www.smashingmagazine.com/2015/05/how-to-use-autoloading-and-a-plugin-container-in-wordpress-plugins/
Which one would be the best way to load them via composer?
This largely depends on whether you want to move your classes to a sub-directory or not. Let's assume you did, and let's assume you were using Toally\Amazing as a namespace prefix for your classes:
PSR-4
Use the PSR-4 autoloader:
{
"autoload-dev": {
"Totally\\Amazing\\": "src/"
}
}
For reference, see:
https://getcomposer.org/doc/04-schema.md#psr-4
Classmap
List the directory which contains the classes:
{
"autoload-dev": {
"classmap": [
"src/",
]
}
}
For reference, see:
https://getcomposer.org/doc/04-schema.md#classmap
Decide for yourself which makes the most sense for you!
Autoloading in production
As you can see, I use the autoload-dev section to indicate that composer autoloading is only relevant for development, not for production. You would still need to
require classes in plugin.php or
implement autoloading for your classes in plugin.php
Take a look at
https://www.smashingmagazine.com/2015/05/how-to-use-autoloading-and-a-plugin-container-in-wordpress-plugins/
which provides a corresponding example for autoloading classes from your plugin.
I'd like to make my first large project in php. I use Phalcon PHP and I created project structure using Phalcon Developer Tools. It something like:
.
├── app
│ ├── cache
│ ├── config
│ │ ├── config.php
│ │ ├── loader.php
│ │ └── services.php
│ ├── controllers
│ ├── migrations
│ ├── models
│ └── views
├── index.html
└── public
├── css
├── files
├── img
├── index.php
├── js
└── temp
I think i'll need some global function and classes. I'd like to implement for example Laravel's dd function to dumb variables and using this function like
dd($value);
wherever i want. I also want to create some global classes to use theirs static functions. For example:
User::isLogged()
How to implement this in my project? Create directory functions or lib or indcude in app/? Is it a convention? Place global classes in individual folders? How to separate global functions and classes and register those in standard Phalcon loader and do it once for whole project?
Good thing about Phalcon is that you have the freedom to organize your project in the way it best fits your current situation.
A more general approach I'm using in most of my projects is to register the most used namespaces in the autoloader. In my case I'm using multi module structure and this is done in the Module.php file for the given module.
Module class:
class Module
{
public function registerAutoloaders($di)
{
$config = $di->getConfig();
$loader = new \Phalcon\Loader();
$namespaces = [
'Frontend\Controllers' => __DIR__ . '/controllers/',
'Frontend\Forms' => __DIR__ . '/forms/',
'Models' => $config->site->path->common . 'models/',
'Helpers' => $config->site->path->common . 'helpers/',
];
$loader->registerNamespaces($namespaces);
$loader->register();
}
}
Helpers in my case are files which are not models and serve for something specific. For example I have a Files helper which holds functions for manipulating the file system. I've got a Helper for handling string operations like slugalization, latinization and so on...
I've also have a Lib folder in which I put my public libraries like PHPMailer, BrowserDetect, ImageProcessing libraries and so on.
And now about the global functions like Laravel's dd(). I have a small file which I include in the bootstrap file or your index.php. It contains 1-2 global functions like:
function d($what)
{
echo '<pre>';
print_r($what);
die('</pre>');
}
In my case there are not that many global functions which I want to use everywhere easily, like above one for debugging. Rest of the stuff I've put in the Helper files mentioned above.
Hope I helped and would be glad to hear someone else's opinion on this.
Quote from Autoloading Classes :
Many developers writing object-oriented applications create one PHP
source file per class definition. One of the biggest annoyances is
having to write a long list of needed includes at the beginning of
each script (one for each class).
In PHP 5, this is no longer necessary. The spl_autoload_register()
function registers any number of autoloaders, enabling for classes and
interfaces to be automatically loaded if they are currently not
defined. By registering autoloaders, PHP is given a last chance to
load the class or interface before it fails with an error.
Here comes the question, what if there are multiple classes in a single php file, is it suitable for autoload usage? or do I have to use require filepath statement?
For example, I have a protocol file under Protobuf\Client.php:
<?php
namespace Protobuf;
class A {
...
}
class B {
...
}
You would have to have some complex function to autoload those classes from the file named Client.php. The idea is to translate your namespace\classname into a directory\filename.php
In this instance you would need to name your file A.php then when you call new Protobuf\A() it will find it. Otherwise you will have to create an overly-complex autoloader.
Let's say you do create the autoloader so it finds the A class, then you can have B on the same file, but only if you have already autoloaded A otherwise you have to make some algorythm to know that A and B are on the same page.
I would do the above pattern or the pattern adopted by apps like Magento that turn class names into directory paths by replacing underscores:
$class = new Core_Classes_MyClass_Client();
Your autoloader would replace the underscores and will load:
Core/Classes/MyClass/Client.php //or similar scheme
This to me is an easy way to do it, but I prefer using namespace and class. The above method is not in favor at the moment and from a naming standpoint, very easy to get mixed up since a lot of classes may be in the same folder or nested really deep into sub folders. You could get some really long naming for classes.
To answer this question directly, we can use classmap for autoload in composer.json to support the single file containing multiple class.
For example we are going to support the single file Protobuf\Client.php, which has two class A and B in it:
<?php
namespace Protobuf;
class A {
}
class B {
}
We add classmap in the composer.json as following:
{
"name": "hailong/myproj",
"autoload": {
"classmap": [
"Protobuf/Client.php"
]
}
}
Then, in main.php we can use class A and B:
<?php
require __DIR__.'/vendor/autoload.php';
use Protobuf\A;
use Protobuf\B;
$a = new A();
$b = new B();
Finally, don't forget to run composer dump-autoload after changing the composer.json, which will magically generate the autoload.php that required in our main.php.
For people not quit familiar with the composer yet, here is the final files structure we will get:
myproj % tree
.
├── Protobuf
│ └── Client.php
├── composer.json
├── main.php
└── vendor
├── autoload.php
└── composer
├── ClassLoader.php
├── LICENSE
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
├── autoload_static.php
└── installed.json
3 directories, 12 files
To extend on Rasclatts very informative answer,
Ideally, it's always good practice to separate classes when it comes to autoloading. I strongly recommend looking into composers PSR-0 Namespace Autoloading
PSR-0 Allows you to beautifully organise all your classes into sub folders with infinite depth, take the following folder structure
\system
- Members
- Members.php
- Auth
- Auth.php
- Database
- Database.php
For this example, in each of the php files above you would have namespace MyNameSpace; before your class declaration and then in your composer.json you would have something similar to (documentation):
"autoload": {
"psr-0": { "MyNameSpace": "/system" }
}
Composer should be installed on your local/host computer for you to compile your autoload files, open terminal and navigate to your project directory and type:
composer dump-autoload -o
Now everything is neatly organised and you can access your classes similar to:
\MyNameSpace\Auth::staticFunction();
Yes And NO,
But what if I do generate (automaticaly) classes and methods for a very large WSDL ?
ie. for a more than hundred methods You would have probably hundred methodRequests (as a class object), next hundred methodResponse (as a Class Object) and large arrays ie ClassMap.
Sometimes it's better to handle that stuff in one file, especialy when developing without good docs for WSDL.
I want to create an API to communicate with a REST Web Service.
For this, I'm building this API as a library. I Just create the folder structure:
├── composer.json
├── LICENSE
├── README.md
└── src
└── PkgRoot
└── PkgName
├── XXXAPIFactory.php
├── XXXAPI.php
├── XXXAPIRestImpl.php
├── XXXResponse.php
└── Protocol.php
Now, I'm trying add a configuration for the API-KEY. I believe this should be added in config.yml.
Should I change all these structure the be like a Symfony Bundle? How can I add and register configurations parameters in config.ym?
It is not mandatory, but I think that you definitely should. Why aren't you using a Bundle ?
Using a Bundle would permit you, among so many other cool things, to create a friendly configuration as you want.
I am trying to make a view on my package, and this is my code:
View::make("User::login");
But I get this error: No hint path defined for [User].
My structure is like this:
app
├──config
├──database
├── modules
└── Core
└── User
├──Controllers
├──models
└──views
└──login.blade.php
├── lang
├── migrations
└── routes.php
The view namespaces actually have nothing to do with PSR-4. You also have to add view directories manually. You can either do that by adding it to the paths array in config/view.php or by registering it somewhere else (preferably in a service provider)
View::addLocation('/path/to/views');
To come back to your actual question, you can register view namespaces like this:
View::addNamespace('User', '/path/to/views');
i've resolved my problem.
it was my fault, it should be :
View::make("Core/User::login");
thanks.