silverstripe-framework/docs/en/04_Changelogs/4.1.0.md
Damian Mooyman 8d077203d4 API Implement support for public/ webroot folder (#7741)
* API Implement support for public/ webroot folder

* Bugfixes and refactor based on feedback
2018-01-12 16:25:02 +13:00

7.3 KiB

4.1.0

Overview

  • Support for public webroot folder public/
  • Better support for cross-platform filesystem path manipulation

Upgrading

Upgrade public/ folder

This release allows the maintenance of a public webroot folder which separates all web-accessible files from protected project files (like the vendor folder and all of your PHP files). This makes your web hosting more secure, and less likely to leak information through accidentally deployed files like a project README. New projects will default to this behaviour. Existing projects (updating from 3.x or 4.0) will continue working as-is, but we strongly recommend switching to the public webroot structure in order to get the security benefits.

This folder name is not configurable, but is turned on by creating this folder, and off by ensuring this folder doesn't exist.

When separating the public webroot from the BASE_PATH it is necessary to move a few files during migration:

  • Move .htaccess from base to public/
  • Move index.php from base to public/
  • Move assets folder (including the nested assets/.protected folder) into public/. This is the only folder which needs write permissions.
  • Ensure that the public/resources folder exists; If this folder already exists in root, you should delete this, and re-generate it by running composer vendor-expose in your root path.
  • Any public assets committed directly to your project intended to be served directly to the webserver. E.g. move mysite/javascript/script.js to public/javascript/script.js. You can then use Requirements::css('javascript/script.js'); / <% require css('javascript/script.js') %> to include this file.
  • Ensure that the web-root configured for your server of choice points to the public/ folder instead of the base path. E.g. an apache virtualhost configuration would look like:
<VirtualHost *:80>
  ServerName mywebsite.com
  ServerAlias *.mywebsite.com
  VirtualDocumentRoot "/var/www/Sites/mywebsite/public/"
</VirtualHost>

You may also need to add various changes to your code if you reference the BASE_PATH directly:

  • You should use Director::publicFolder() instead of Director::baseFolder() if referring to the public folder.
  • You can check if a public folder exists with Director::publicDir()

Example public/ folder structure

For example, this is an existing folder structure:

/var/www/mysite
├── assets/
│   └── .protected/
├── mysite/
│   ├── code/
│   │   ├── Page.php
│   │   └── PageController.php
│   └── css/
│       └── projectstyle.css
├── resources/ _(auto-generated by vendor-plugin `composer vendor-expose` command)_
│   └── silverstripe/
│       └── blog/
│           └── css/ _(symlink)_
│               └── blog.css
├── themes/
│   └── mytheme/
│       ├── css/
│       │   └── theme.css
│       └── templates/
│           └── BlogPage.ss
├── vendor/
│   └── silverstripe/
│       └── blog/
│           ├── css/ _(exposed in blog composer.json)_
│           │   └── blog.css
│           └── composer.json
├── .htaccess
├── composer.json
├── favicon.ico
├── index.php
└── install.php

After migration the folder structure would look like:

/var/www/mysite
├── mysite/
│   └── code/
│       ├── Page.php
│       └── PageController.php
├── public/
│   ├── assets/
│   │   └── .protected/
│   ├── css/
│   │   └── somestyle.css
│   ├── resources/ _(auto-generated by vendor-plugin `composer vendor-expose` command)_
│   │   ├── themes/
│   │   │   └── mytheme/
│   │   │       └── css/ _(symlink)_
│   │   │           └── theme.css
│   │   └── vendor/
│   │       └── silverstripe/
│   │           └── blog/
│   │               └── css/ _(symlink)_
│   │                   └── blog.css
│   ├── .htaccess
│   ├── favicon.ico
│   ├── index.php
│   └── install.php
├── themes/
│   └── mytheme/
│       ├── css/ _(exposed in root composer.json)_
│       │   └── theme.css
│       └── templates/
│           └── BlogPage.ss
├── vendor/
│   └── silverstripe/
│       └── blog/
│           ├── css/ _(exposed in blog composer.json)_
│           │   └── blog.css
│           └── composer.json
└── composer.php

Use new $public theme set

In addition there is a new helper pseudo-theme that you can configure to expose files in the public/ folder to the themed css / javascript file lookup. For instance, this is how you can prioritise those files:

---
Name: mytheme
---
SilverStripe\View\SSViewer:
  themes:
    - '$public'
    - 'simple'
    - '$default'

This would allow <% require themedCSS('style.css') %> to find a file comitted to public/css/style.css.

Note that Requirements calls will look in both the public folder (first) and then the base path when resolving css or javascript files. Any files that aren't in the public folder must be exposed using the composer.json "expose" mechanism described below.

Expose root project files

If you have files comitted to source control outside of the public folder, but you need them to be available to the web server, you can also use the composer.json expose directive to symlink / copy these to public/resources/.

composer.json (in project root)

{
  "extra": {
    "expose": [
      "mysite/client"
    ]
  }
}

Then run the composer helper composer vendor-expose in your project root. This will symlink (or copy) the mysite/client directory to public/resources/mysite/client.

If you are using third party modules which may not have explicit expose directives, you can also expose those assets manually by declaring the full path to the directory to expose. This works the same for silverstripe-module and silverstripe-vendormodule types.

{
  "extra": {
    "expose": [
      "vendor/somevendor/somemodule/css",
      "anothermodule/css"
    ]
  }
}

For more information on how vendor modules work please see the documentation on the vendor plugin page or the publishing a module documentation.

Path manipulation helpers

The following filesystem helpers have been added in order to better support working with cross-platform path manipulation:

  • SilverStripe\Core\Convert::slashes() to convert directory separators to either / or \
  • SilverStripe\Core\Path::join() which will join one or more relative or absolute paths.
  • SilverStripe\Core\Path::normalise() which will normalise and trim directory separators in a relative or absolute path

For example: normalising Convert::normalise('/some\\dir/') will convert to /some/dir. Setting the second arg to true will also trim leading slashes. E.g. Convert::normalise('/sub\\dir/', true) will convert to sub/dir.

It is preferrable to use these helpers in your code instead of assuming DIRECTORY_SEPARATOR === /