Difference between #5 and #4 of Moving project code outside of webroot (plus multiple project support)

changed
Title
How to set up directory structures forMoving project code
outside of webroot (plus multiple Yii project
supportsupport)
unchanged
Category
How-tos
unchanged
Tags
config, security
unchanged
Content
Yii's by-default directory organization works well enough, but there are
several steps one can take that improve the security and serviceability of the
system, especially in the context of multiple Yii projects on the same machine
(including multiple versions of the _same_ project).

In this article we'll use three versions of the same blog project:
production, test, and development, and presumably changes migrate from the last
to the first. The precise top-level folder doesn't matter, and though this
example uses `/var/www/`, it could be anywhere else.

* `/var/www/blog/`
* `/var/www/blog-test/`
* `/var/www/blog-dev/`

These instructions were written assuming Yii 1.1 on a Linux server with Apache.

Make symbolic links to the Yii framework code.
----------------------------------------------
Since the Yii framework can be shared among projects, our normal practice is to
put the Yii code up at the top level with the full version information in the
directory name.

* `/var/www/yii-1.1.4.r2429/`
* `/var/www/yii-1.1.5.r2654/`
* ...

and then symbolic link from within the top-level of the project:

    # cd /var/www/blog
    # ln -s /var/www/yii-1.1.5-r2654 yii

Now, `/var/www/blog/yii/` refers to the proper framework, and this is a fixed
relative position within the particular project.

This also makes it easy to upgrade just one project at at time by changing the
symbolic link.

Move `protected/` out of the webroot
------------------------------------
Nothing in the `protected/` folder needs to be fetched directly by a webuser, so
it's a needless security risk to put where it has to be protected with
mechanisms that are easy to get wrong (`.htaccess`): this is just asking for
trouble with no upside.

Instead, move the `protected/` folder to the same level as the `webroot/`
folder, so there is no possibility of accessing it via the browser.

Move `runtime/` out of `protected/`
-----------------------------------
The `protected/` folder (even after moving) is composed almost entirely of
source code, except for the `runtime/` folder, which is used by Yii for logs,
state information, and the like.  Though it's reasonable that Yii have such
an area, it just doesn't really belong with the source code.

So my practice is to move it out of `protected/` as well, up at the same level
as the webroot. 

Resulting structure
-------------------
After making these changes, the Yii directory structure looks like:

    /var/www/blog/webroot/...
    /var/www/blog/webroot/css/...
    /var/www/blog/webroot/assets/...
    /var/www/blog/protected/...
    /var/www/blog/runtime/...
    /var/www/blog/yii  --> symlink to /var/www/yii-1.1.5.r2654

The `protected/` folder is now composed entirely of source code, is invisible to
the web user, and the `runtime/` area is separate from everything else.

Config file changes
-------------------
We certainly have to tell Yii where these parts are, and this involves editing
both the main config file, plus the application entry script.

In the config file we should document well what we're doing (to make it
easier for others to follow), and a small helper function assists by joining
paths together and removing spurious `/./` and `/../` components. Though we
don't currently use the `$webrootPath` variable, adding it anyway helps
give a fuller context to where all the parts fit together.

**NOTE** - some have suggested using the `DIRECTORY_SEPARATOR` constant instead
of literal /, but it's not clear that this is really necessary even on a
Windows machine. Those who find a case where it really does matter are
encouraged to Edit this page to add your findings.
~~~
[php]
// in protected/config/main.php
...
// Set up path variable to reflect the new directory structure
//   $WEBHOME/             -- $homePath
//   $WEBHOME/webroot/     -- $webrootPath
//   $WEBHOME/protected/   -- $protectedPath
//   $WEBHOME/runtime/     -- $runtimePath

function _joinpath($dir1, $dir2) {
    return realpath($dir1 . '/' . $dir2);
}

$homePath      = dirname(__FILE__) . '/../..';
$protectedPath = _joinpath($homePath, 'protected');
$webrootPath   = _joinpath($homePath, 'webroot');
$runtimePath   = _joinpath($homePath, 'runtime');

return array(
    'basePath'    => $protectedPath,
    'runtimePath' => $runtimePath,
    ...
);
~~~
These variables may be used in other places as well, such as configuring the
location of the `authFile` for `CPhpAuthManager`

The entry script in `webroot/index.php` must be taught where to find both the
`protected/` folder as well as the Yii framework, and it's easy to make
these modifications:
~~~
[php]
...
$yii    = dirname(__FILE__) . '/../yii/framework/yii.php';
$config = dirname(__FILE__) . '/../protected/config/main.php';
...
~~~
Because these paths are used only once, there's no real need to optimize
with the `realpath()` function as was done in the configuration file itself.

**NOTE** - if you have console-mode programs, you'll need to make similar
`$yii` and `$config` changes in that entry script as well, though the exact form
of the path changes depends on where your entry scripts are located.

Permissions
-----------
After making all these structural changes, do ensure that the key folders have
the same permissions as before. In particular, two folders must be writable by
the webserver user, and this is achieved with a combination of user, group, and
permissions:

* `webroot/assets/`
* `runtime/`