I was wondering what the best practices were for handling configuration files for your scripts when using Composer.
To elaborate, if I have a script that has several options or settings I'd usually just add them as variables or constants in a config.php file or something similar, and then require the file my script. Or, if the script is a simple class then just use class properties... basic stuff, right?
So, my question is, with the advent of PSR-4, autoloading, Composer, etc., what is the preferred way of doing this?
It feels wrong to ask users to directly edit the file(s) in vendor/ (eg: to edit a config file or properties in a class, etc.). Plus, wouldn't those edits get overwritten on updates?
I thought about using defined() in my script to look for certain constants, which the user could set in their own config files. This would probably work fine for smaller projects, but might become cumbersome and hard to scale. For some scripts it might also be difficult to come up with sane defaults in the event that the constants aren't defined by the user.
I also thought about using one of Composer's "hooks" to trigger a script that moves a config file somewhere like the project's root directory. But then I hate to just drop a random config file in the user's project...
Am I overthinking this? How do you guys typically handle this situation?
Don't have a configuration file. Let the code which includes your project set configuration values at run time.
If you are working in object oriented php you may want to take any required configuration parameters as arguments to your constructor or factory method, and provide setter methods for optional parameters, or parameters for which a sensible default value exists.
Related
I'm new to php and inherited a website project with hundreds of pages, all procedural (when I do a text search of the files, there isn't even a function definition anywhere). I'm coming from the c# and Java worlds. I'm looking for a way to incrementally add OOP. (They want me to update the front end and I am trying to convince them of fixing the backend at the same time and they don't want to use a framework (dammit)).
While looking into autoloader... Well, here's my understanding. It's a method of registering folders where classes are stored and when you instantiate a class, trait, etc. it searches the folder based on the class/filename/namespace and loads the appropriate definitions.
I have a few questions:
Does autoloader search the folder and load the appropriate definitions on every page lifecycle (or does it cache them)?
Pre-loading:
Is there a way to use autoloader, or some alternative, to pre-load ALL class definitions into memory and make them available across all sessions?
If so, when updating class files, how would I tell this mechanism to reload everything to memory when I make changes to class files?
UPDATE TO QUESTIONS:
Thank you both for your answers and it helps a little, but... I do have a bad habit of posing the wrong question(s) on StackOverflow.
The thing I want to avoid is slowing down pages by adding classes. So let's say I add a library and register the paths with autoloader. A page instanciates a class with multiple dependencies. Let's say that the dependency graph includes 15 files. For each request lifecycle, the server loads the page and 15 other files just on that one page.
Since I am coming from compiled languages, I feel a little strange not loading these classes into memory. All the classes together should not be over say 5MB.
(Or maybe I should just create a RAM Disk and copy all the files in there on boot and just have a symlink?)
Auto loaders in PHP are lazy. When PHP encounters a the use of a class it doesn't know about, it will ask the registered autoloader (or chain of autoloaders) to go find it. It's the autoloader's job to figure out where to get the file the class is defined in and include it. Having some sort of convention for naming your classes and organizing your class files is key to having a useful autoloader, and several conventions have arisen in the PHP community, such as PSR-4.
Does autoloader search the folder and load the appropriate definitions on every page lifecycle (or does it cache them)?
The autoloader(s) is(are) called on every request, but only when the need to autoload a class arises.
Pre-loading: Is there a way to use autoloader, or some alternative, to pre-load ALL class definitions into memory and make them available across all sessions?
I don't believe so, but as the number of classes grow, this becomes more and more wasteful.
Welcome to the wonderful[citation needed] world of legacy PHP, I highly recommend you check out Modernizing Legacy Applications In PHP. It's like a strategy guide for getting from Mordor back to the Shire.
I think you may misunderstand the purpose of autoloading. It is simply instructions on what to do when your code calls for a class that PHP doesn't recognize. That's it. The autoloader just calls requires /path/to/classfile so that PHP will see the class.
Does autoloader search the folder and load the appropriate definitions
on every page lifecycle (or does it cache them)?
There is no caching across requests, so if you make a change to file, the next http request will incorporate those changes. It's just as if you changed any other instruction in your script, for example change echo 1 to echo 2
Pre-loading: Is there a way to use autoloader, or some alternative, to
pre-load ALL class definitions into memory and make them available
across all sessions?
There is no need for this. A well written autoloader has instructions for where to find any class, so loading all possible classes ahead of time is wasteful. If you're still running into undefined classes errors, you need to either improve the autoloader or place the class files in accordance with the current autoloader instructions.
If you really want to preload all your classes, use the auto_prepend_file setting in php.ini. The docs say
Specifies the name of a file that is automatically parsed before the
main file
Set it to an initialization script. In that script have something like:
//put all your class files in this folder
$dir = '/path/to/classes/folder';
$handle = opendir($dir);
//require all PHP files from classes folder
while (false !== ($item = readdir($handle))){
$path = $dir.'/'.$item;
if(is_file($path) && pathinfo($path,PATHINFO_EXTENSION)==='php')
require_once $path;
}
This is simplified. There is significant risk in just including all files in any directory into your script so I would not do this. You would also need to adjust this if you want to include files in subdirectories.
Basically, don't do this. Just have a good autoloader.
No one posted what I was looking for but it seems the best route is the OptCache that's prebuilt into php 5.5 and above (my client is using 5.3 so I didn't know about it).
https://github.com/zendtech/ZendOptimizerPlus
The Zend OPcache
The Zend OPcache provides faster PHP execution through opcode caching
and optimization. It improves PHP performance by storing precompiled
script bytecode in the shared memory. This eliminates the stages of
reading code from the disk and compiling it on future access. In
addition, it applies a few bytecode optimization patterns that make
code execution faster.
I'm writing a web application and I would like to know how to initialize all constant (such as db connection data, directories etc), classes and so on.
I read about bootstrap file but I'm not sure I understand the technique ( more details here: https://stackoverflow.com/questions/9774105/htaccess-and-bootstrap-file).
A more specific case would be better. In this case, your question on settings, there are different approaches. Generally a config file is most used in projects. That way it can easily be excluded from version control systems.
Bootstrap files are not the location to store settings.
So bootstrap load the config file and following to that the bootstrap initiates the system.
Generally a config file is the most accepted choice.
For paths there are different parts. You have in-app paths. Keep them in your code, your system should be able to find the right files. Might be done with a PHP Autoloader for example.
Then you have paths to resources like images, pdf files and other data. Keep that part strongly separated from your application. Preferably via a class which handles all those files. That way you are free to move them to another server, move them to Amazon for example when your project grows etc.
So don't try to hardcode paths and keep things separated.
I'm writing my first PHP app. Everyone talks about having a bootstrap.php to initialize your app. This makes sense and I've put together one that I'm happy with. There's two things I don't understand, and which no one seems to mention:
Where do I call my boostrap from? Do I include it in every page? Please tell me there's a better way...
How do I make it so my bootstrap is not called more often than needed? I assume it only needs to be called either at the start of the app or the start of a new session.
1: Generally the bootstrap is in the "application" directory. It is called from the "public" directory which is in the same level as application (not inside of it). The index.php inside the public folder should include the bootstrap.php and that is where your Document_Root should be set to (note you may need to change / add some include paths for it to work)
2: It should only be included once via the index.php file in the public folder. Just including it there should be enough, if it was done correctly.
Hope that helps.
It depends on your application architecture.
If your architecture is the good old "flock of php scripts" - PHP scripts called directly from the browser - then you'll be including it at the top of each script, one way or another.
Most developers (and frameworks) these days marshall all their requests through /index.php one way or another, usually with some URL rewriting going on to make nice, pretty URLs for users to see.
In this day and age, you should probably be doing the latter, or at least thinking about it. It leads to much better organization, and even more importantly, allows you to keep all your code outside of the web server's document root, which is a good security practice for several reasons that are outside the scope of this answer.
Have a look at the singleton pattern. You can double your bootstrap class as a resource container, e.g.:
$bootstrap = Bootstrap::getInstance();
$dbConn = $bootstrap->getPdoDbh();
You can include or require the file, or use the autoloader and make sure you have a call to instantiate the object on all your pages. You might even have a call to getInstance() on the bottom of the file, after the class definition.
Or you might use URL-based routing and have all your requests go through a single index.php file, like Zend Framework does. Or better yet, use Zend Framework.
This answer assumes you're doing OOP w/ PHP >=5, which really is the way to go.
One of the more elegant means by which to bootstrap a PHP application is to do so using Composer.
Almost every PHP library uses Composer nowadays, and requiring a Bootstrap.php-like file is as simple as:
"autoload": {
"psr-4": {
"Acme\\Rocket\\": "src/"
},
"files": ["src/Bootstrap.php"]
},
Note the second property, files. (The first, psr-4, is standard PSR-4 boilerplate auto-loading, and is included only to make the example more real-world.)
Including the bootstrap file in this way doesn't make the naive assumption that the PHP application is executed in a web-server context, via index.php, or similar; the application could very well be a command-line application (or both, like Laravel/Artisan). Bootstrapping via the auto-loader makes this distinction a non-issue.
it depends on what your bootstrap file does. If it's just a file that sets some ini settings and such to create a sane execution environment and establish a database connection, then simply including it with require_once in your scripts should be enough. If it's more of a single-point of entry then you can configure your server to filter all requests to it and have it dispatch to appropriate controller scripts.
I have a directory of PHP scripts ( mainly utilities), that is located on C:\Development, whereas my Symfony application is located in D:\Development.
How to include the utility scripts into the Symfony application? I try not to use include() because I prefer a symfony-like command.
There are basically two approaches
Extend the autoloading
modify the include path
#2 can be achieved numerous ways. If you are unsure how to, let me know and I'll post some details.
You don't have to change php.ini. You can use ini_set or set_include_path. Check ricardo's comment in the User Contributed Notes section at the documentation for set_include_path().
Personally, I'd create a new folder inside the lib/vendor folder on your project, put your scripts in there, and then symfony should handle it all automatically assuming you've followed its naming conventions for files/classes. If not, you may need to alter the autoload stuff as per Peter Bailey's answer.
I prefer this way because I like to be able to deploy the project from one server to another as one atomic unit with no reliance on external dependancies. This may not be an issue for you though, in which case the existing answer will suit you better.
I'm in the process of evaluating the benefits of Zend_Config_Ini versus using a simple constant file.
e.g. -
define('DB_HOST',localhost);
//versus
$config = new Zend_Config_Ini('/path/to/config.ini', 'staging');
echo $config->database->params->host; // prints "dev.example.com"
The only thing is that the $config is not globally accessible. So then you need to use Zend_Registry to store for application usage, without having to initiate each time.
This seems to add more complexity than needed.... am I missing something or is Zend_Config + Zend_Registry a technique that is better in the long run as an app grows?
The main advantage of using the Registry with a config is to avoid polluting the global namespace. Say, you want to include a third party lib and your app and the lib both define a constant DB_HOST. No good.
In addition, many of the factories in Zend Framework utilize the config object to create instances. Zend_Db is a good example of this. You just pass it $this->database and it will take what it needs to instantiate your adapter.
You can also extend the registry with custom functionality, e.g. finder methods or stuff like that. Check the pattern description by Fowler.
A nice advantage of Zend_Config is that you don't depend on a "PHP source code file" ; just a .ini file will do -- and some prefer modifying an .ini file instead of a PHP one.
And it's easier to modify an .ini / XML file programatically - there are classes / functions to do that !
With .ini files and Zend_Config, you also have nice functionnalities already provided ; for instance, inheritance between sections (ie, you can have a "generic" section, and "staging" and "production" that overwrite some values)
A thing that can be insteresting about Zend_Config, too, is consitency : Zend Framework, with Zend_Application, already supposes you'll have one configuration file ; so, why not a second one ? Or even re-use the first one ?
And it's the same with several classes of the Framework, which can work or be configured with an instance of Zend_Config ; if you already have that one, it suddenly becomes easier ;-)
If I had to choose between a PHP file with defines and a .ini file, for things that are configurable, I would probably go with the .ini file (And Zend_Config + Zend_Registry when needed).
Yes, you're correct, using the Zend sanctioned method for configuration is more complex than defining a series of global constants. The advantages you get are
No Global Namespace Collisions
If you ever need to integrate your application with another project, having global constants represent your application config may cause problems. What are the chances another project has a constant named DB_HOST? How can a developer who's using your hybrid system tell which constants are the config for your application vs. the integrated application?
Building on the Work of Others
So, you have a single file with all your config values. How are you going to deploy that into different environments? (production, staging, etc.) Chances are you're a smart person who could come up with a solution, but how is another developer coming into your project going to know how that solution works? How are other modules in your application going to know how to read your global config?
Zend_Config has already "solved" many of the problems. By taking on a bit more complexity and abstraction up-front you get a known path forward, and can spend more time on solving the problems of your application instead of inventing and supporting Yet Another Configuration System.