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

unchanged
Title
How to set up directory structures for multiple Yii project support
unchanged
Category
How-tos
unchanged
Tags
config, security
changed
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/` arearea 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/`