В прошлых уроках я показал вам, как создать систему управления содержимым плоской файловой системы (CMS) с использованием Go , Node.js и Ruby .
В этом уроке я собираюсь взять ту же модель проектирования и построить сервер с использованием PHP . Поскольку PHP сам по себе не является сервером, а обычно связан с веб-сервером Apache , я покажу вам, как настроить веб-сервер Apache в виртуальной системе Vagrant .
Настройка и загрузка библиотек
Для начала вам нужно установить PHP в вашей системе. Перейдите на сайт PHP и загрузите версию для своей системы. В большинстве дистрибутивов Linux и во всех системах Mac OS X уже установлен PHP.
Затем установите Composer , менеджер пакетов для PHP, аналогичный npm для Node.js. Чтобы установить Composer, введите в терминале следующее:
|
1
|
php -r “readfile(‘https://getcomposer.org/installer’);” |
|
После установки Composer установка библиотек для сервера будет следующей. Сначала создайте каталог для проекта с файлами из раздела Построение CMS: структура и стилизация или распакуйте загрузку для этого руководства. После настройки перейдите в этот каталог и введите:
|
1
2
3
4
5
|
composer require slim/slim “^3.0”
composer require erusev/parsedown
composer require zordius/lightncandy:dev-master
composer require “talesoft/tale-jade:*”
composer install
|
Эти строки устанавливают четыре библиотеки, составляющие сервер: Slim Router — это библиотека маршрутизации для обработки входящих запросов к серверу, Parsedown переводит Markdown в HTML, Handlebars — это библиотека шаблонов, а Jade Library — это сокращенная форма HTML, которая Я использую, чтобы сделать страницы индекса для сообщений.
Создание index.php
В верхней части каталога проекта создайте файл index.php. Сервер Apache ожидает, что главная страница сайта будет иметь это имя. В этом файле поместите следующий код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?php
//
// Program: phpPress
//
// Description: This is a full flat file
// system CMS that mimics the
// organization of goPress.
// Since goPress server can not
// be run on a shared server,
// phpPress will work there and
// offer easy migration to
// goPress if the site is moved
// to a VPS.
//
require ‘vendor/autoload.php’;
//
// Load libraries used.
//
//
// HandleBars: https://github.com/zordius/lightncandy
//
use LightnCandy\LightnCandy;
//
// Jade Library: https://github.com/Talesoft/tale-jade
//
use Tale\Jade;
|
Оператор require позволяет PHP знать, как загружать различные библиотеки, установленные с помощью Composer. Затем вы говорите PHP использовать библиотеки LightnCandy и Jade.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
//
// Slim Router: http://www.slimframework.com/
//
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
$app = new \Slim\App;
//
// Set an Error Handler.
//
$c = $app->getContainer();
$c[‘errorHandler’] = function ($c) {
return function ($request, $response, $exception) use ($c) {
return $c[‘response’]->withStatus(400)
->withHeader(‘Content-Type’, ‘text/html’)
->write(ProcessPage($parts[‘layout’],“ErrorPage”));
};
};
//
// This line will cause Slim not to catch errors.
// production server.
//
unset($app->getContainer()[‘errorHandler’]);
|
Затем вы настраиваете маршрутизатор Slim, создавая экземпляр объекта приложения Slim и устанавливая обработчик ошибок. С установленным обработчиком ошибок вы никогда не получите отладочную информацию при возникновении ошибки. Поэтому во время разработки вам нужно сбросить обработчик ошибок. Но для производства закомментируйте последнюю строку с unset.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
//
// Get the server.json information in to global site variables.
//
$site = __DIR__ .
$style = “Basic”;
$layout = “SingleCol”;
$styleDir = ‘./themes/styling/’ .
$layoutDir = ‘./themes/layouts/’ .
$parts = Array();
$parts[“CurrentLayout”] = “SingleCol”;
$parts[“CurrentStyling”] = “Basic”;
$parts[“ServerAddress”] = “http://localhost:8080”;
$parts[“SiteTitle”] = “Test Site”;
$parts[“Sitebase”] = “./site/“;
$parts[“TemplatBase”] = “./themes/“;
$parts[“Cache”] = false;
$parts[“MainBase”] = ““;
//
// Load relevant items in the layouts directory.
//
$parts[“layout”] = file_get_contents($layoutDir . ‘/template.html’);
//
// Load relevant items in the styles directory.
//
$parts[“404”] = file_get_contents($styleDir . ‘/404.html’);
$parts[“footer”] = file_get_contents($styleDir . ‘/footer.html’);
$parts[“header”] = file_get_contents($styleDir . ‘/header.html’);
$parts[“sidebar”] = file_get_contents($styleDir . ‘/sidebar.html’);
$parts[“ErrorPage”] = “<h1 class=’Error’>There was a server error!</h1>“;
//
// Load everything in the parts directory.
//
$d = dir($site . ‘/parts/’);
while (false !== ($entry = $d->read())) {
if((strcmp($entry,“..“)!=0)&&(strcmp($entry,“.“)!=0)) {
$pathparts = pathinfo($entry);
$parts[basename($pathparts[‘filename’])] = figurePage($site . ‘/parts/’ . $pathparts[‘filename’]);
}
}
$d->close();
|
Следующий раздел кода — создание хеш-таблицы $parts для хранения фрагментов информации, используемых в шаблонах. Затем программа добавляет все содержимое каталога частей сайта в хеш-таблицу. Таким образом, вы можете создавать многократно используемые фрагменты для добавления на любую страницу.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
//
// Function: SetBasicHeader
//
// Description: This function will set the basic header
// information needed.
//
// Inputs:
// $response The response object to be
// sent to the browser.
//
function SetBasicHeader($response) {
$newResponse = $response->withAddedHeader(“Cache-Control”, “max-age=2592000, cache”);
$newResponse = $newResponse->withAddedHeader(“Server”, “phpPress — a CMS written in PHP from Custom Computer Tools: http://customct.com.“);
return($newResponse);
}
|
Функция SetBasicHeader() устанавливает заголовок возврата для всех страниц. Это устанавливает функции управления кэшем и имя сервера. Если вам нужна дополнительная информация заголовка, это то место, где вы ее установите.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
//
// Array of shortcodes and their functions.
//
$shcodes = Array(
‘box’ => function($args, $inside) {
return(“<div class=’box’>” . $inside . “</div>“);
},
‘Column1’ => function($args, $inside) {
return(“<div class=’col1′>” . $inside . “</div>“);
},
‘Column2’ => function($args, $inside) {
return(“<div class=’col2′>” . $inside . “</div>“);
},
‘Column1of3’ => function($args, $inside) {
return(“<div class=’col1of3′>” . $inside . “</div>“);
},
‘Column2of3’ => function($args, $inside) {
return(“<div class=’col2of3′>” . $inside . “</div>“);
},
‘Column3of3’ => function($args, $inside) {
return(“<div class=’col3of3′>” . $inside . “</div>“);
},
‘php’ => function($args, $inside) {
return(“<div class=’showcode’><pre type=’syntaxhighlighter’ class=’brush: php’>” . $inside . “</pre></div>“);
},
‘js’ => function($args, $inside) {
return(“<div class=’showcode’><pre type=’syntaxhighlighter’ class=’brush: javascript’>” . $inside . “</pre></div>“);
},
‘html’ => function($args, $inside) {
return(“<div class=’showcode’><pre type=’syntaxhighlighter’ class=’brush: html’>” . $inside . “</pre></div>“);
},
‘css’ => function($args, $inside) {
return(“<div class=’showcode’><pre type=’syntaxhighlighter’ class=’brush: css’>” . $inside . “</pre></div>“);
}
);
|
Хеш-таблица $shcodes содержит все функции шорткода для обработки элементов на веб-странице. Функции, которые я здесь написал, просты, но при необходимости они могут быть более сложными. Это дает возможность встраивать более динамичный код в ваши веб-страницы.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
//
// Function: processShortcodes
//
// Description: This function will expand all
// shortcodes in the page given to
// it.
//
function processShortcodes($page) {
global $shcodes;
$result = ““;
while(preg_match(“/\-\[(\w+)(.*)\]\-/i”, $page, $match) == 1) {
$num = count($match);
$command = $match[1];
$cmdarg = ““;
if($num > 2) {
$cmdarg = $match[2];
}
$spos = strpos($page,“-[{$command}“);
$result .= substr($page, 0, $spos);
$page = substr($page,$spos + 4 + strlen($command) + strlen( $cmdarg));
$sepos = strpos($page,“-[/{$command}]-“);
$inside = trim(substr($page, 0, $sepos));
if(strcmp($inside,““) != 0) {
$inside = processShortcodes($inside);
}
$page = substr($page, $sepos + 5 + strlen($command));
//
// If the command name exists in the
// shortcodes hash table, then run the
// function.
//
if( array_key_exists($command, $shcodes) ) {
$result .= call_user_func($shcodes[$command], $cmdarg, $inside);
}
}
$result .= $page;
return($result);
}
|
Функция processShortCodes() найдет, выполнит и вставит результаты processShortCodes() . Эта функция находит все шорткоды на странице, рекурсивно вызывая себя для вложенного содержимого и остальной части страницы.
Шорткод имеет следующий синтаксис:
|
1
2
3
|
-[name arg]-
contents
-[/name]-
|
Имя — это имя шорткода, arg — аргументы, переданные шорткоду, а contents — это часть страницы, которая заключена в шорткод. -[ и ]- действуют так же, как < и > в HTML.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
//
// Create the HandleBar helpers array.
//
$helpers = Array(
‘flags’ => LightnCandy::FLAG_HANDLEBARS |
‘helpers’ => Array(
‘save’ => ‘save_helper’,
‘date’ => ‘date_helper’,
‘cdate’ => ‘cdate_helper’
)
);
//
// Function: save_helper
//
// Description: This helper will save the given text to
// the given name.
// latter in the document.
//
// Inputs:
// $args The arguments sent to the
// helper function.
//
function save_helper($args) {
global $parts;
$arg = implode(” “, $args);
$hparts = explode(“|“, $arg);
if(count($hparts) == 2) {
$parts[$hparts[0]] = $hparts[1];
return $hparts[1];
} else {
return $parts[$hparts[0]];
}
}
//
// Function: date_helper
//
// Description: This function formats the current date
// according to the formatting string given.
//
// Inputs:
// $args The arguments sent to the helper
//
function date_helper($args) {
$dateFormat = implode(” “, $args);
return date($dateFormat);
}
//
// Function: cdate_helper
//
// Description: This function formats the date given
// according to the formatting string given.
//
// Inputs:
// $args The arguments sent to the helper
//
function cdate_helper($args) {
return date($args[0], $args[1]);
}
|
В следующем разделе содержатся вспомогательные функции для добавления помощников в механизм шаблонов Handlebars. Вспомогательные функции: save , date и cdate . Функция save берет имя и текст. Везде, где имя указано в макросе, данный текст заменит его. Функция date берет текущую дату и форматирует ее в соответствии с заданной строкой форматирования. Функция cdate берет дату и строку форматирования. Он установит указанную дату в заданный формат.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
//
// Function: ProcessPage
//
// Description: This function will process a page into
// the template, process all Mustache
// macros, and process all shortcodes.
//
// Inputs:
// $layout The layout for the page
// $page The pages main contents
//
function ProcessPage( $layout, $page ) {
global $site, $parts, $helpers;
//
// We need a temporary file for creating the
// Handlebars rendering function.
// need to change this depending on your system.
//
$php_inc = “/var/tmp/handlebarsTemp.php”;
//
// Get the page contents.
//
$parts[‘content’] = figurePage($page);
//
// First pass on Handlebars.
//
$phpStr = LightnCandy::compile($layout, $helpers);
file_put_contents($php_inc, $phpStr);
$renderer = include($php_inc);
$page = $renderer($parts);
//
// Process the shortcodes.
//
$pageShort = processShortcodes($page);
//
// Second pass Handlebars.
//
$phpStr = LightnCandy::compile($pageShort, $helpers);
file_put_contents($php_inc, $phpStr);
if($phpStr != ““) {
$renderer = include($php_inc);
$page = $renderer($parts);
}
//
// Return the results.
//
return($page);
}
|
Следующая функция — ProcessPage() . Эта функция принимает макет страницы и содержимое страницы. Он объединит их с помощью движка шаблонов LightnCandy Handlebars. Затем на полученной странице ищутся шорткоды. Полученная страница после этого снова запускается через LightnCandy. Браузер получает вывод от LightnCandy.
|
01
02
03
04
05
06
07
08
09
10
|
//
// Setup the routes routines.
//
function page( $pageAddress ) {
global $site, $parts;
$page = ProcessPage( $parts[‘layout’], “{$site}/pages/{$pageAddress}“);
return($page);
}
|
Функция page() определяет страницу для отправки пользователю. Функция получает адрес страницы от маршрутизатора и вызывает ProcessPage() для создания страницы.
|
1
2
3
4
5
6
|
function posts($postType, $blog, $pageAddress ) {
global $site, $parts;
$page = ProcessPage( $parts[‘layout’],“{$site}/posts/{$postType}/{$blog}/{$pageAddress}“);
return($page);
}
|
Функция posts() работает так же, как функция page() для содержимого posts() . Маршрутизатор передает значения типа записи, блога и адреса страницы из маршрута. Затем эта функция использует ProcessPage() чтобы создать страницу для возврата.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
function figurePage( $page ) {
global $site, $parts;
$result = ““;
if(isset($parts[$page])) {
//
// A site partial was specified.
//
$result = $parts[$page];
}else if(file_exists(“{$page}.html”)) {
//
// It is a html piece.
//
$result = file_get_contents(“{$page}.html”);
} else if(file_exists(“{$page}.md”)) {
//
// It is a markdown piece.
// it on.
//
$Parsedown = new Parsedown();
$result = file_get_contents(“{$page}.md”);
$result = $Parsedown->text($result);
$result = str_replace(“"“,“\““,$result);
} else if(file_exists(“{$page}.amber”)) {
//
// It is a Jade (using the golang name for the
// extension) page.
//
$jade = new Jade\Renderer();
$jade->addPath(dirname($page));
$result = $jade->render(basename($page));
} else {
$result = $parts[“404”] ;
}
//
// give the resulting page content.
//
return($result);
}
|
Функция figurePage() возвращает правильное содержимое страницы на основе заданного имени. Функция будет искать файл с расширением .html. Если он есть, он читает его и отправляет вызывающей подпрограмме.
Затем функция ищет расширение .md. Если он есть, он читает его, преобразует Markdown в HTML и возвращает его в вызывающую подпрограмму. Далее, функция видит, существует ли такая с расширением .amber. Если он есть, он читает его, преобразует синтаксис Jade в HTML и возвращает его вызывающей функции.
|
1
2
3
4
5
6
7
8
|
//
// This route handles the main or home page.
//
$app->get(‘/’, function(Request $request, Response $response) {
$newResponse = SetBasicHeader($response);
$newResponse = $newResponse->getBody()->write(page(‘main’));
return($newResponse);
});
|
Эта функция маршрута отображает главную или домашнюю страницу веб-сайта. Это сработает для всех запросов к доменному имени с или без ‘/’.
|
01
02
03
04
05
06
07
08
09
10
11
|
//
// This route handles the favicon loading.
//
$app->get(‘/favicon.ico’, function(Request $request, Response $response){
global $site;
$newResponse = SetBasicHeader($response);
$newResponse = $newResponse->withAddedHeader(“Content-type”, “image/ico”);
$newResponse->getBody()->write(file_get_contents(“{$site}/images/favicon.ico”));
return($newResponse);
});
|
Это определение маршрута соответствует конкретному запросу: /favicon.ico. Это дает значок для веб-сайта. Возвращает изображение «/images/favicon.ico» в каталоге сайта.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
//
// This route handles all the stylesheets loading as one
// sheet.
//
$app->get(‘/stylesheets’, function(Request $request, Response $response) {
global $site;
$newResponse = $response->withHeader(“Content-type”, “text/css”);
$newResponse = SetBasicHeader($newResponse);
$newResponse->getBody()->write(file_get_contents(“{$site}/css/final/final.css”));
return($newResponse);
});
|
Этот маршрут получает скомпилированную таблицу стилей и возвращает ее запрашивающей стороне. Скомпилированная таблица стилей всегда называется «/css/final/final.css».
|
01
02
03
04
05
06
07
08
09
10
11
|
//
// This route handles the loading of the scripts.
//
$app->get(‘/scripts’, function(Request $request, Response $response) {
global $site;
$newResponse = $response->withAddedHeader(“Content-type”, “text/javascript”);
$newResponse = SetBasicHeader($newResponse);
$newResponse->getBody()->write(file_get_contents(“{$site}/js/final/final.js”));
return($newResponse);
});
|
Этот маршрут всегда возвращает скомпилированный файл JavaScript, найденный в «/js/final/final.js».
|
01
02
03
04
05
06
07
08
09
10
11
12
|
//
// This route handles all the image routes.
//
$app->get(‘/images/{image}’, function(Request $request, Response $response) {
global $site;
$ext = pathinfo($request->getAttribute(‘image’), PATHINFO_EXTENSION);
$newResponse = SetBasicHeader($response);
$newResponse = $newResponse->withAddedHeader(“Content-type”, “image/$ext”);
$newResponse->getBody()->write(file_get_contents(“{$site}/images/” . $request->getAttribute(‘image’)));
return($newResponse);
});
|
Этот маршрут обрабатывает все запросы на изображения. {image} указывает коду маршрутизатора дать имя образу функции. Вызов функции $request->getAttribute() возвращает значение.
|
01
02
03
04
05
06
07
08
09
10
11
|
//
// This route handles all the video items.
//
$app->get(‘/videos/{video}’, function(Request $request, Response $response) {
global $site;
$newResponse = SetBasicHeader($response);
$newResponse = $response->withAddedHeader(“Content-type”, “video/mp4”);
$newResponse->getBody()->write(file_get_contents(“{$site}/video/” . $request->getAttribute(‘video’)));
return($newResponse);
});
|
Этот маршрут получает все видео-запросы и отправляет их в браузер.
|
1
2
3
4
5
6
7
8
|
//
// This route handles all the blog posts pages.
//
$app->get(‘/posts/blogs/{blog}’, function( Request $request, Response $response) {
$newResponse = SetBasicHeader($response);
$newResponse->getBody()->write(posts(“blogs”,$request->getAttribute(‘blog’), “index”));
return($newResponse);
});
|
Этот маршрут возвращает список записей блога и их резюме. Переменная {blog} будет именем блога, для которого будут перечислены записи.
|
1
2
3
4
5
|
$app->get(‘/posts/blogs/{blog}/{post}’, function( Request $request, Response $response) {
$newResponse = SetBasicHeader($response);
$newResponse->getBody()->write(posts(“blogs”,$request->getAttribute(‘blog’), $request->getAttribute(‘post’)));
return($newResponse);
});
|
Этот маршрут получает отдельную запись в блоге. {blog} — это название блога, а {post} — это запись в блоге, которую нужно получить.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
//
// This route handles all the news posts pages.
//
$app->get(‘/posts/news/{news}’, function( Request $request, Response $response) {
$newResponse = SetBasicHeader($response);
$newResponse->getBody()->write(posts(“news”,$request->getAttribute(‘news’), “index”));
return($newResponse);
});
$app->get(‘/posts/news/{news}/{post}’, function( Request $request, Response $response) {
$newResponse = SetBasicHeader($response);
$newResponse->getBody()->write(posts(“news”,$request->getAttribute(‘news’), $request->getAttribute(‘post’)));
return($newResponse);
});
|
Запросы маршрутизатора новостей работают так же, как запросы маршрутизатора блога.
|
01
02
03
04
05
06
07
08
09
10
|
//
// This route will process all the pages.
// catch all types of routes other than the home page, this
// route should handle the unknown pages as well.
//
$app->get(‘/{page}’, function( Request $request, Response $response) {
$newResponse = SetBasicHeader($response);
$newResponse->getBody()->write(page($request->getAttribute(‘page’)));
return($newResponse);
});
|
Это общий маршрут страницы. Все запросы, которые не соответствуют ни одному из предыдущих запросов, будут инициировать этот запрос.
|
1
2
3
4
5
6
|
//
// Respond to requests.
//
$app->run();
?>
|
Этот последний бит кода запускает сервер. Эта функция выполнит всю обработку запроса. По возвращении интерпретатор PHP завершит и закроет соединение с браузером пользователя.
Этот PHP-сервер работает совершенно иначе, чем другие серверы этой серии. Эта процедура запускается и заканчивается для каждого запроса, в то время как другие серверы продолжают обрабатывать запросы. Следовательно, этот PHP-сервер занимает больше времени для обработки запросов из-за загрузки и выгрузки подпрограмм.
Подготовка сервера
Теперь настроить сервер. Один из самых простых способов получить стек Apache и PHP — использовать Vagrant . Чтобы установить на Mac, используйте Homebrew с этой командой:
|
1
|
brew cask install vagrant
|
Если у вас есть система Windows или Linux, используйте установщики с веб-сайта Vagrant. На веб-сайте также есть автономный установщик для Mac, но, установив Homebrew, вы можете получать автоматические обновления, выполнив:
|
1
|
brew update
|
После установки создайте файл с именем Vagrant в каталоге проекта. В этом файле поместите следующий код:
|
1
2
3
4
5
6
|
Vagrant.configure(“2”) do |config|
config.vm.box = “ubuntu/trusty64”
config.vm.provision :shell, path: “bootstrap.sh”
config.vm.network :forwarded_port, host: 8080, guest: 8080
config.vm.synced_folder “/full/path/to/code/directory”, “/vagrant”
end
|
Это файл конфигурации Vagrant. Он сообщает Vagrant, на какую виртуальную машину создать компоновку, какой скрипт нужно запустить, чтобы настроить блок для вашего сервера, какие порты на виртуальной машине сопоставить с портами на вашем главном компьютере, а также папку для синхронизации с папкой / vagrant в виртуальная машина. Вам нужно установить полный путь к каталогу на вашем компьютере вместо /full/path/to/code/directory .
Затем создайте файл bootstrap.sh и добавьте в него этот скрипт:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
sudo apt-get update
sudo apt-get install -y apache2
#
# Setup modules in apache that will be needed.
#
sudo ln -s /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/headers.load
sudo ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load
#
# Setup apache on port 8080 and configure apache to read the .htaccess files for
# access changes.
#
sudo cat /etc/apache2/sites-available/000-default.conf |
sudo mv /var/tmp/sites.conf /etc/apache2/sites-available/000-default.conf
sudo cat /etc/apache2/ports.conf |
sudo mv /var/tmp/ports.conf /etc/apache2/ports.conf
sudo cat /etc/apache2/apache2.conf |
sudo mv /var/tmp/apache2.conf /etc/apache2/apache2.conf
#
# setup php5
#
sudo apt-get install -y php5
#
# Setup the server root directory
#
sudo rm -rf /var/www/html
sudo ln -fs /vagrant /var/www/html
#
# reload apache
#
sudo service apache2 reload
|
Этот скрипт загрузится на веб-сервер Apache и настроит его. После настройки Apache он устанавливает PHP версии 5, устанавливает корневой каталог для сервера и запускает службу Apache. Все это настроит виртуальную машину так, как необходимо для этого проекта, и будет иметь корневой каталог сервера, получающего файлы из каталога вашего проекта. Любые изменения, которые вы вносите в каталог проекта, мгновенно становятся доступными для сервера.
В каталоге проекта создайте файл с именем .htaccess и поместите этот код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
# 1 YEAR
<FilesMatch “\.(ico|pdf|flv)$“>
Header set Cache-Control “max-age=29030400, public”
</FilesMatch>
# 1 WEEK
<FilesMatch “\.(jpg|jpeg|png|gif|swf)$“>
Header set Cache-Control “max-age=604800, public”
</FilesMatch>
# 2 DAYS
<FilesMatch “\.(xml|txt|css|js)$“>
Header set Cache-Control “max-age=172800, proxy-revalidate”
</FilesMatch>
# 1 MIN
<FilesMatch “\.(html|htm|php)$“>
Header set Cache-Control “max-age=60, private, proxy-revalidate”
</FilesMatch>
|
Это сообщает серверу Apache, как обрабатывать запросы для этого сайта и для автоматического кэширования запросов. Файл index.php получает каждый запрос к серверу.
Запуск сервера
Чтобы запустить сервер, выполните следующую команду в каталоге проекта:
|
1
|
vagrant up
|
Это загрузит виртуальную машину, если она еще не установлена в вашей системе, запустит сценарий инициализации и запустит сервер. Для просмотра сайта откройте браузер по адресу http: // localhost: 8080 .

Браузер должен показать вышеуказанную страницу. Сервер теперь онлайн и готов к вашим дополнениям.
Если веб-сайт недоступен для просмотра, вам следует проверить разрешения для всех файлов на сайте. OS X часто имеет их только для чтения. Вам нужно сделать их читаемыми во всем мире с помощью следующей команды в каталоге проекта:
|
1
|
chmod -R a+r .
|
Это обеспечит возможность чтения файлов сервером Apache на виртуальной машине.
Чтобы остановить сервер, выполните следующую команду в каталоге проекта:
|
1
|
vagrant halt
|
Чтобы узнать больше об использовании Vagrant, пожалуйста, прочитайте документацию Vagrant . Вы также можете ознакомиться со многими учебниками по Vagrant здесь, на Envato Tuts +. Я использовал инструмент Hobo для создания моих Vagrant-файлов. Это отличный инструмент для настройки, запуска и управления системами Vagrant в OS X.
Вывод
Теперь, когда вы знаете, как создать простой, но мощный веб-сервер с использованием языка PHP, пришло время экспериментировать. Создавайте новые страницы, сообщения, встраиваемые части и шорткоды. Эта простая платформа намного быстрее, чем WordPress, и она полностью под вашим контролем.
В отличие от других веб-серверов этой серии, эта CMS на основе PHP может работать на любой учетной записи общего хостинга. Я использую его для веб-сайта моего министерства на DreamHost . Я не оптимизировал изображения, но это все еще довольно хорошо. Расскажите мне о вашем сервере в комментариях ниже.