문제

In my application architecture I want to replace my globals with something that ain't gonna burn most of the developer's eyes, because I am using globals like this,

define('DEVELOPMENT_ENVIRONMENT', true);

// Shorten DIRECTORY_SEPARATOR global,
define('DS', DIRECTORY_SEPARATOR);

// Set full path to the document root
define('ROOT', realpath(dirname(__FILE__)) . DS);

how could I prevent this? I tried creating a class that reads an xml file, but this will give me a longer code like this

$c = new Config();
if($c->devmode === TRUE) {}

or maybe something like this

$c = new Config()
echo $c->baseurl;

Any better ways to do this?

도움이 되었습니까?

해결책

I think questions like yours can not be generally answered but they probably deserve an answer anyway. It's just that there is not the one golden rule or solution to deal with this.

At the most bare sense I can imagine the problem you describe is the context an application runs in. At the level of human face this is multi-folded, just only take the one constant:

define('DEVELOPMENT_ENVIRONMENT', true);

Even quite simple and easily introduced, it comes with a high price. If it is already part of your application first try to understand what the implications are.

You have one application codebase and somewhere in it - in concrete everywhere the constant is used - there are branches of your code that are either executed if this constant is TRUE or FALSE.

This on it's own is problematic because such code tends to become complex and hard to debug. So regardless how (constant, variable, function, class) you first of all should reduce and prevent the usage of such constructs.

And honestly, using a (global) constant does not look that wrong too me, especially compared with the alternatives, it first of all is the most preferable one in my eyes because it lies less and is not complicated but rather straight forward. You could turn this into a less-dynamic constant in current PHP versions by using the const keyword to declare it however:

const DEVELOPMENT_ENVIRONMENT = TRUE;

This is one facet of this little line of code. Another one is the low level of abstraction it comes with. If you want to define environments for the application, saying that a development environment is true or false is ambiguous. Instead you normally have an environment which can be of different types:

const ENVIRONMENT_UNSPECIFIED = 0;
const ENVIRONMENT_DEVELOPMENT = 1;
const ENVIRONMENT_STAGING     = 2;
const ENVIRONMENT_LIVE        = 3;

const ENVIRONMENT = ENVIRONMENT_DEVELOPMENT;

However this little example is just an example to visualize what I mean to make it little ambiguous. It does not solve the general problem outlined above and the following one:

You introduce context to your application on the level of global. That means any line of code inside a component (function, class) that relates to anything global (here: DEVELOPMENT_ENVIRONMENT) can not be de-coupled from the global state any longer. That means you've written code that only works inside that applications global context. This stands in your way if you want to write re-usable software components. Re-usability must not only mean a second application, it already means in testing and debugging. Or just the next revision of your software. As you can imagine that can stand in your own way pretty fast - or let's say faster then you want.

So the problem here is less the constant on it's own but more relying to the single context the code will run in or better worded global static state. The goal you need to aim for when you would like to introduce changes here for the better is to reduce this global static state. This is important if you're looking for alternatives because it will help you to do better decisions.

For example, instead of introducing a set of constants I have in the last code-example, find places that you make use of DEVELOPMENT_ENVIRONMENT and think why you have put it in there and if it is not possible to remove it out there. So first think about if it is needed at all (these environment flags are often a smell, once needed in a quick debugging or because it was thought "oh how practical" - and then rotting in code over weeks of no use). After you've considered whether it is needed or not and you came to the point it is needed, you need to find out why it is needed at that place. Does it really belong there? Can't it - as you should do with anything that provides context - turned into a parameter?

Normally objects by definition ship with their own context. If you've got a logger that behaves differently in development than in live, this should be a configuration and not a decision inside the application code somewhere. If your application always has a logger, inject it. The application code just logs.

So as you can imagine, it totally depends on many different things how and when you can prevent this. I can only suggest you to find out now, to reduce the overall usage.

There are some practical tips on the way for common scenarios we face in applications. For the "root-path problem" you can use relative paths in conjunction with magic constants like __DIR__. For example if the front-endpoint in the webroot (e.g. index.php) needs to point to the private application directory hosting the code:

<?php
/**
 * Turbo CMS - Build to race your website's needs to the win.
 *
 * Webroot Endpoint
 */
require(__DIR__ . '/../private/myapp/bootstrap.php');

The application then normally knows how it works and where to find files relative to itself. And if you return some application context object (and this must not be global(!)), you can inject the webroot folder as well:

<?php
/**
 * Turbo CMS - Build to race your website's needs to the win.
 *
 * Webroot Endpoint
 */

/* @var $turboAppContext Turbo\App\WebappContext */
$turboAppContext = require(__DIR__ . '/../private/myapp/bootstrap.php');
$turboAppContext->setWebroot(__DIR__);

Now the context of your webserver configures the application defaults. this is a crucial part actually because this touches a field of context inside your application (but not in every component) that is immanent. You can not prevent this context. It's like with leaking abstractions. There is an environment (known as "the system") your application runs in. But even though, you want to make it as independent as possible.

Like with the DEVELOPMENT_ENVIRONMENT constant above, these points are crucial to reduce and to find the right place for them. Also to only allow a very specific layer to set the input values (to change context) and only some high-level layers of your software to access these values. The largest part of your code-base should work without any of these parameters. And you can only control the access by passing around parameters and by not using global. Then code on a level that is allowed to access a certain setting (in the best meaning of the word), can access it - everything else does not have that parameter. To get this safety, you need to kill globals as best as possible.

E.g. the functionalitly to redirect to another location needs the base-url of the current request. It should not fetch them from server variables but based on a request-object that abstracts access to the server variables so that you can replace things here (e.g. when you're moving the application behind a front-proxy - well not always the best example but this can happen). If you have hard-coded your software against $_SERVER you would then need to modify $_SERVER in some stages of your software. You don't want that, instead you move away from this (again) global static state (here via a superglobal variable, spot those next to your global constants) by using objects that represent a certain functionality your application needs.

As long as we're talking about web-applications, take a look at Symfony's request and response abstraction (which is also used by many other projects which makes your application even more open and fluent). But this is just a side-note.

So whatever you want to base your decision on, do not get misguided by how many letters to type. The benefit of this is very short-sighted when you start to consider the overall letters you need to type when developing your software.

Instead understand where you introduce context, where you can prevent that and where you can't. For the places you can't, consider to make context a parameter instead of a "property" of the code. More fluent code allows you more re-usable code, better tests and less hassles when you move to another platform.

This is especially important if you have a large installation base. Code on these bases with global static state is a mess to maintain: Late releases, crawling releases, disappointed developers, burdensome development. There are lessons to learn, and the lessons are to understand which implications certain features of the language have and when to use them.

The best rule I can give - and I'm not an academic developer at all - is to consider global as expensive. It can be a superb shortcut to establish something however you should know about the price it comes with. And the field is wide because this does not only apply to object oriented programming but actually to procedural code as well. In object oriented programming many educational material exists that offers different ways to prevent global static state, so I would even say the situation there is quite well documented. But PHP is not purely OOP so it's not always that easy as having an object at hand - you might first need to introduce some (but then, see as well the request and response abstractions that are already available).

So the really best suggestion I can give to improve your code in context of this question is: Stick to the constant(s) (maybe with const keyword to make them less dynamic and more constant-ly) and then just try to remove them. As written in comments already, PHP does a very fine job about cross-platform file-access, just use / as directory separator, this is well understood and works very well. Try to not introduce a root-path constant anyway - this should not be constant for the code you write but a parameter on some level - it can change, for example in sub-requests or sub-apps which can save you a life-span before re-inventing the wheel again.

The hard task is to keep things simple. But it's worth.

다른 팁

Just put some server variable to the vhost config and prepare different config files for each option. Using apache it would be (you'll need mod_env module):

SetEnv ENVIRONMENT dev

And then in index just use something like:

$configFileName = getenv ('ENVIRONMENT').'.ini';

Now just load this file and determine all the application behaviour on the values given. Ofcourse you can facilitate it further if you use some framework but this would be a good start.

You can encapsulate your constants in a class and then retrieve it by a static methods :

if(Config::devMode()) {}

echo Config::baseUrl();

This way you save a line and some memory because you don't need to instantiate an object.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top