In my application I have my constants:
define('APP_PATH', WEB_ROOT . APP_DIR);
define('LIBS_PATH', APP_PATH . LIBS_DIR);
define('MODELS_PATH', APP_PATH . MODELS_DIR);
define('VIEWS_PATH', APP_PATH . VIEWS_DIR);
define('CONTROLLERS_PATH', APP_PATH . CONTROLLERS_DIR);
etc...
because they will never be changing once my application starts and they are simple to access from within any class/method.
I have a config file too with other settings which gets imported into a $config object that I pass around my application and retrieve them like:
$this->config->setting('some.setting');
I have never had to change a config value at the end or in the middle of my application so wouldn't it be easier to just define them as constants so I can access them anyway in my code easily?
I don't want to be statically retrieving settings either i.e
Config::setting('some.setting');
I have looked at the code of a few PHP frameworks and they all define paths as constants but then have other config settings in some sort of Config class even though as far as I can see they never change those config settings throughout the code (they might though as I have not read through every line of the tens of thousands) and lots of those frameworks seem to love doing static calls to all sorts of methods from within methods in different classes and people say they are good frameworks but I've read and experienced more bad than good when it comes to static calls within classes/methods.
What do you think is best to do with the config settings? What do you do?
Wrapping constants in objects makes your code more portable.
This way you can load constants at application bootstrap from a file for example, so you can reuse your code in an arbitrary number of apps, each with a different configuration file.
To another extent, loading stuff from a class acts as a façade: you can move settings from a file to a database, to hardcoding them even, and the whole application won't notice.
You can go with define for very little projects of course.
Wrapping settings in a config object provides encapsulation, which allows better coexistence.
For instance, suppose you have a framework for database access. If you used global settings for this, you couldn't easily access multiple databases in a single program. Putting the settings into an object allows you to use a particular config object for the corresponding database.
Related
I'm relatively new to PHP, and I'm looking for a way to define certain objects as globally accessible from throughout the project, from multiple PHP scripts.
In Java, if I've to access some objects globally, I define some public class named Globals , and define the objects that I need to access as static in the Globals class. I then access those objects wherever I need with: Globals.variable_name .
So basically, I need to initialize these global objects only once and then use them whenever I need them..
One use case:
I've a class named Logger that has methods to log certain events in a log file. I need to have 1 Logger instance/object that can be used by all the PHP scripts in the project, whenever they've to log something. I'd rather not have each PHP script using it's own instance of Logger.
The naive Java-like approach I tried, that did not work:
I created a public class named Globals in a separate PHP file (named Globals.php) with one static object of type Logger, named $logHandle. I included this PHP file in all other PHP files where I need this Logger object. I then tried to access this object , using Globals->logHandle from one of the other PHP scripts. This attempt failed miserably.
Is there some similar approach?
Thanks.
PHP is not Java.
In web applications, the PHP environment is initialized for each request - and each request is handled in a different process with it's own memory space. It is possible to share access to data (including serialized objects, but not resources such as database connections and file handles) across different instances. You probably know this already but have not yet realised how it influences the way you write code.
I'd rather not have each PHP script using it's own instance of Logger
Why not?
One very good reason is that allowing multiple processes to write to the same open file handle requires locking to prevent the file getting all mesed up. BUT THIS IS PHP - STOP REINVENTING THINGS FROM SCRATCH. Writing to stderr will append the details to the webserver error log or use the OS syslog facilities - that's what they are there for.
It is impossible to have the same object available to all instances of PHP - you can unserialize an object in all instances - but then it's not the same object. You can run a daemon with a single object which might be accessible to all other PHP instances via a socket connection - but it's not running in the same address space.
If you validly have a class that you want to be universally available via an object with a fixed name, then simply create an instance of the object in each script or via an include file. The approach you tried is the way to go about this (but don't name your objects with reserved words). We don't know why it failed because you didn't provide any error messages or code.
I assume you're asking about common case (now only web-oriented application). And for that - no, you can not define some thing like you've described, in native way. This is the thing that is called superglobals in PHP.
But on the other hand - you need to do that for some reason, not "just because you want it". And, if so - then use configuration file. Create some application configuration file, read it once at start of application (bootstrap) and you'll get all needed values. I'm not saying anything about file structure - it can be xml/ini/yaml/whatever/you/like. But the idea is - to split this logic from application structure itself and use separate file for storing desired values.
Another option is to use some separate PHP file(s) and include it at bootstrap. Define all needed variables/constants in that file(s) and you'll get similar behavior. Note, that in terms of namespaces it's less "global" and you'll need to resolve all that cases manually.
For web-applications, however, one of possible solutions may be using sessions. $_SESSION is a superglobal array and it will behave like you want (i.e. will be accessible from everywhere). But that is not applicable always - and not always you'll want to deal with sessions to store session-independent data.
you can do like this
you said that you have included in all other classes change methods in your global class to static
<?php
class Logger {
public static function log($msg) {
// ...
}
}
you can use it like
Logger::log($msg);
http://www.php.net/manual/en/reserved.variables.globals.php
i think that is what you're after.
To access a static attribute in PHP you need to call it with the Class::$attribute notation, and the static methods need to be called with the Class::method() notation.
The -> notation is used when calling attributes of a class instance.
I want to add an application constant like YII_DEBUG. I found that it should be defined in YiiBase class. But I don't know what is the best way to do so.
This is what the Yii documentation says
Yii is a helper class serving common framework functionalities.
It encapsulates YiiBase which provides the actual implementation. By writing your own Yii class, you can customize some functionalities of YiiBase.
How should I write my own Yii class, where should I put it and where should I call it?
You can define global constants in quite a few places that will get them defined automatically during request processing; the choice depends on what makes the most sense. Your options include:
The index.php entry script -- the constants will be usable throughout your app.
The configuration file, before you write return array(...) -- the constants will be usable throughout your app but not before Yii::createApplication (or the equivalent createWebApplication) is called.
The application class definition itself (wherein they can also be class constants instead of global constants). Again, the constants will be usable only after createApplication is called in the entry script.
If you want to define a constant as universally applicable as YII_DEBUG you should do as Yii does and pick option #1.
It depends on exactly what you are trying to do with this class top determine the best practice with it. The easiest way would be to set up your new class file in the vendors folder and call it, whatever, myclass.php. Then, you need to make sure is picking up the vendors folder in your config/main.php file, like so:
Yii::import('application.vendors.*');
require_once('myclass.php');
Finally, you should be able to call your new class from almost anywhere within the application, like so:
$myclass = new myclass();
$myclass->thisMethod();
There are, of course, other ways to do it. Depending on what you want you custom class to do, you might be better to use a different method, but this way will definitely get you up and running fast.
We have recently been rewriting our PHP application's model code using OO. We have the classes done, and we included namespacing (in the form of Project\Level1\Level2 or whatever). Since this is an already-built application, there are a lot of classes. We are using an autoloader, for ease of loading the classes, which is great. But here's where the hassle comes in.
We have probably 300 pages strewn all across the application, and all of them require a PHP file at the very beginning (a sort of bootstrap script). The autoloader script is required in that bootstrap file.
Now, most of our pages will be using at least one but likely many of the classes. We know that we have two options here:
//either call the classes using qualified names
$person = new Project\Person;
//or include the "use" keyword on every page, so we can alias the classes for ease of use
use Project\Person as Person;
$person = new Person;
But the hassle is that we would really rather not have to do this on every single page in our application:
require_once('../php/bootstrap.php');
use Project\Person\ as Person,
Project\Address\ as Address,
Project\Group\ as Group
Project\CustomField\ as CustomField;
$person = new Person;
$address = new Address;
We tried, for the heck of it, including the use keyword for all our classes in the bootstrap file, but according to the PHP manual, "Importing rules are per file basis, meaning included files will NOT inherit the parent file's importing rules."
It just seems like including these sprawling use statements on every page of our application is creating more hassle than it's worth. And what if we are using 15 classes on one page? Then the use statement would be huge.
My question is this: is it too much hassle? Are we correct in using namespaces, or is it not all that helpful in PHP? And now knowing what we are trying to do, are we doing it right? Is there something we are missing? Should we be calling classes with fully qualified names all the time or something?
(this is my first question on stackoverflow, so please let me know if I need to do anything differently or better with my questions in the future)
While using vanilla Zend Framework Autoloader might not be they way you want to go, the Zend Loader does support custom autoloaders, that can respect namespaces specified in-line or dynamically at run-time. In your case, you are using use Project\Person\ as Person which can become cumbersome, as you already noticed.
Using namespaces with the autoloader will allow you to take advantage of auto loading using simple path traversal to organize classes, without having to append large paths just to get to the interesting parts.
So, instead of new Corp_Assets_Generic_Person_Address, you could keep the folder structure and use new Namespace_Person_Address.
Namespaces avoid clashes in class names. Specially in large applications implementing autoloaders that need to load the right class from a number of different libraries. Using shorter class names makes the code easier to read but may make it harder to maintain. Adding the use statements makes it easier to read and later maintain but then it would be cumbersome to write and refactor. Documenting the expected behavior of the autoloader and using custom autoloaders to make more readable class names would be my suggestion.
In php.ini add your file as a prepend_file
I'm writing my first basic bare bones MVC patterns setup in PHP. I know global variables are bad, and I aslo know I don't want all of my classes to have access to all of my config vars.
I have a settings.php file that I would like to define a bunch of constants in, like my db connection info, directory structure info, email addresses and so on. It will be one centralized location that holds all of my important information.
What's the best way to implement a config class so that each of my base classes for my controller and model only have access to the config vars they need? For example, my model base class should have access to by db connection info.
Basically I am just asking how anybody whole rolls their own MVC setup handles config information without declaring global variables, like we used to back in the procedural days.
Thanks.
You're going to get a bunch of answers on this as it basically boils down to preference.
Personally, ive used a config array. example:
$conf['db']['username'] = "username";
$conf['db']['password'] = "password";
Then just pass byref the pieces you need into where they need to go.
I rolled my own MVC setup. (Still going strong. Might open source it). The way I do it is to have an init script that passes all those constants to a controlling class.
The controlling class is a Singleton. So anytime another class needs access to it, it just gets the existing instance of the controlling class and all the variables are available from it.
When using dependency injection for database handlers etc instead of singleton - where is it best to keep the configs I.e. Username password host etc. Keep inside the class, use a container class or use a static configs class or use a file?
I generally keep them in a file outside of the webroot.
External config file that returns an array is a quick solution:
config.php:
<?php
return array(
'database'=> array(
'host'=> 'localhost',
'dbname'=> 'name_of_db',
'username'=> 'myusername',
'password'=> 'mypassword',
),
);
test.php:
<?php
$config = include('config.php');
mysql_connect($config['database']['host'], $config['database']['username'], $config['database']['password']);
....
Ideally, store the config file in a directory that can be read by the anonymous web user (but not written).
This is difficult to get 'right' because it depends on the exact use-case. But here's what I did when I had a very similar problem.
I had setup a shared library system for a small number of websites. Initially, it provided just a database handler, but there was quickly added an ORM layer. Most of the growth after that was additional objects as one of the websites was rewritten away from direct SQL into object-based access. There was also a configuration library used to define how the objects and other elements in the shared library were collected together into 'modules' as well as default settings for things like database settings. It also supported loading a configuration file outside the code-tree, which was used to per-host settings.
Since the objects in the ORM layer had to configure themselves (there was a static call to do that as they were declared), it was trivial for it to be extended to request a particular database by name, too. Then it was a matter of making sure that all these database definitions were declared as well (and were overridable thanks to the generic configuration mechanism).
(It took a while, but when we did reach the point of having to split the database, it was very easy to point the relevant objects off to another database and everything just kept working.)
To answer your question, though: the configuration was split. Database hostnames, usernames and passwords were all named and defined in one place in the in-code configuration area. But they could be overridden on a per-host basis. Per object settings were where the objects were declared. And that was also where the database configurations were specified by name.