2

I have a a few php files which I call via AJAX calls. They all have a URL to my config.php. Now I've the problem that I always have to change the URLs to that config file by hand when I deploy a new version on my server.

Local Path:

define('__ROOT__', $_SERVER["DOCUMENT_ROOT"].'/mywebsite');

Server Path:

define('__ROOT__', $_SERVER["DOCUMENT_ROOT"].'/../dev.my-website.tld/Modules/');

I want to track changes in all of these PHP files. I'm searching for a solution to automatically change this path.

E.g. This is my current workflow:

Local Environment:

  • (path version A)
  • do changes in the code
  • git add, git commit, git merge, git push to my server

Server:

  • git reset --hard
  • change path to version B
halfer
  • 19,824
  • 17
  • 99
  • 186
Andi Giga
  • 3,744
  • 9
  • 38
  • 68
  • Good question. I've removed the extra question about Gulp, as it is rather an addendum to this question (we prefer one at a time here) and it was rather general. I suspect you will find you won't need it, but if you would like to use it in your deployment process, it probably deserves its own question. – halfer Dec 22 '14 at 10:26

2 Answers2

3

You are trying to run different code bases between development and live, which is not recommended -- they should be identical. The way I tackle this is to use an environment variable to specify which of several config files should be loaded.

In my Apache vhost I do something like this:

SetEnv ENVIRONMENT_NAME local

And then I use a function to read the environment name:

function getEnvironmentName()
{
    $envKeyName = 'ENVIRONMENT_NAME';
    $envName = isset($_SERVER[$envKeyName]) ? $_SERVER[$envKeyName] : null;
    if (!$envName)
    {
        throw new \Exception('No environment name found, cannot proceed');
    }

    return $envName;
}

That environment name can then be used in a config file to include, or to retrieve values from a single array keyed on environment.

I often keep environment-specific settings in a folder called configs/, but you can store them anywhere it makes sense in your app. So for example you could have this file:

// This is /configs/local.php

return array(
    'path' => '/mywebsite',
    // As many key-values as you want
);

You can then do this (assuming your front controller is one level deep in your project, e.g. in /web/index.php):

$root = dirname(__DIR__);
$config = include($root . '/configs/' . getEnvironmentName() . '.php');

You'll then have access to the appropriate per-environment settings in $config.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • But to get access to this function you would also need to specify a path to a "functions.php". Or do you include this php code in every php file? If I got you right I would define two environment vars. Then use this function to get back either "local" or "server". After that I start another function which sets the path according to the environment variable. The environment variable can be set in the .htaccess? EDIT: or maybe simpler if I just define the root path within the htaccess – Andi Giga Dec 22 '14 at 11:18
  • I've added an example of how to get access to the config file. To load libraries, you can use `__DIR__`, which is the directory in which your front controller (index.php) appears. There are several methods to move up the tree from there if you need to, depending on where your front controller is in your project. – halfer Dec 22 '14 at 11:35
  • (So, to answer your question, the answer is no - you only need one env var. You load libraries by reading the location of the called PHP script and work out the full pathname from there). – halfer Dec 22 '14 at 11:40
  • How does __DIR__ behave when I have a structure like this: maindirectory/myapp/moduleA/some-php.php but also maindirectory/myapp/moduleA/SubModuleA1/some-php.php and my config is here: maindirectory/myapp.config.php I guess I need than additionally a ROOT_ENV var to get it from an absolute path? Document_Root points on XAMPP one folder before my app. /Applications/XAMPP/xamppfiles/htdocs/ And on the server on another folder where I first have to head back 1 folder and then move into the folder. /var/www/virtual/myname/html >> /var/www/virtual/myname/mywebsite.tld/ – Andi Giga Dec 22 '14 at 12:21
  • The `__DIR__` magic constant refers to the directory the PHP file is executing in. So if your user has requested `/path/to/maindirectory/myapp/moduleA/some-php.php` then the dir value is `/path/to/maindirectory/myapp/moduleA` (the leafname and slash is stripped off). Your comment question is rather confusing though - do you want to move up more than one level of directory? – halfer Dec 22 '14 at 12:47
  • "I guess I need than additionally a ROOT_ENV var to get it from an absolute path?". No, I've answered that already. You determine your project root using magic constants such as `__DIR__`. That contains the full directory location of the file, and then you can move up the tree with things like `dirname()`. – halfer Dec 22 '14 at 12:51
  • That said, it wouldn't be a _bad_ solution to have another env var for your project root - it is not less maintainable, so use it if you wish. But I don't think it is necessary. – halfer Dec 22 '14 at 12:52
  • It depends. If I start from $_SERVER["DOCUMENT_ROOT"] than I have to move up one level and after that I have to move down. If I use __DIR__ I guess I have to individually set the paths depending how deeply nested my modules are. That was why I used absolute paths from the root, rather than relative from folders to not get an error if I move s.th.. – Andi Giga Dec 22 '14 at 15:56
  • (Use backticks when referring to `__DIR__`, as you can see, it is getting translated as Markdown without). – halfer Dec 22 '14 at 16:31
  • Well, you were asking for my advice, and my advice is to derive a `$root` variable for every front controller you are using. You can work that out in one line of code. Remember that you don't need to go up/down different levels depending on environment; just work out the root, and then anything environment-dependent, like a path, goes in config. Still, all said, you may do it as you wish! – halfer Dec 22 '14 at 16:33
  • Another tip, in case it is useful. If you want to jump back several levels in a file, do this: `$root = realpath(__DIR__ . '/../../..');`, with an appropriate number of `..`. The `realpath()` will do the jump backs for you, and give you the raw directory you want. – halfer Dec 22 '14 at 18:26
1

A pure git way to achieve this would be filters. Filters are quite cool but often overlooked. Think of filters as a git way of keyword expansion that you could fully control.

The checked in version of your file would for example look like this:

define('__ROOT__', 'MUST_BE_REPLACED_BY_SMUDGE');

Then set up two filters:

  • on your local machine, you'd set up a smudge filter that replaces
    'MUST_BE_REPLACED_BY_SMUDGE'
    with
    $_SERVER["DOCUMENT_ROOT"].'/mywebsite'

  • on your server, you'd set up a smudge filter that replaces
    'MUST_BE_REPLACED_BY_SMUDGE'
    with
    $_SERVER["DOCUMENT_ROOT"].'/../dev.my-website.tld/Modules/'

  • on both machines, the clean filter would restore the line to be
    define('__ROOT__', 'MUST_BE_REPLACED_BY_SMUDGE');

Further information about filters could be found in this answer and in the Git Book.

Community
  • 1
  • 1
eckes
  • 64,417
  • 29
  • 168
  • 201
  • Would you check in the initial file (your first snippet) but then also add it to `.gitignore`? Once it has done its expansion locally, you would want to make sure you don't accidentally check in the expanded version. – halfer Dec 22 '14 at 16:36
  • @halfer: you'd check in the version with the placeholder. It's the job of the `clean` filter to ensure that the version with the placeholder gets checked in and the job of the `smudge` filter to introduce the changes for each location. And the .gitignore does not contain the file to modify. It's a regularly checked in file. – eckes Dec 22 '14 at 17:22
  • Right, I don't understand that - seems like I need to add this to my reading list! +1 for an interesting contribution, even though I don't think code modification is an appropriate solution here. – halfer Dec 22 '14 at 17:30
  • (Wouldn't a `git status` show the smudged file as modified in the local environment? And, if so, isn't there a risk that it would get checked in accidentally?) – halfer Dec 22 '14 at 17:31
  • @halfer: it won't be shown as modified. That's part of the magic. And you won't be able to accidently check in unwanted modifications since the clean filter runs before comparing to the index (the git status case) and when adding to the index (the commit case). And yes, read about it. Once you understiod it, you'll discover a wide range of new posibilities that will ease your life. – eckes Dec 22 '14 at 17:50
  • Ooh nice, sounds good. I have a Git book lurking about, I will see if it covers it. – halfer Dec 22 '14 at 18:24
  • 1
    @halfer: take **the** git book: git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#Keyword-Expansion – eckes Dec 22 '14 at 18:26
  • Yeah, I'm just old school - I like dead tree format! But I will read that one too. – halfer Dec 22 '14 at 18:28