Legacy PHP ์์(5.3.29 ์ด์) ์ฌ์ฉํ๊ธฐ ์ํด Apache2 ํ๊ฒฝ์์ ๋์ํ๋ ๋ง๋ ๋งค์ฐ ๊ฐ๋จํ Framework ์ ๋๋ค.
A very simple PHP framework for legacy environments on Apache2.
Legacy PHP (5.3.29 ์ด์) ์์ ๊ตฌ์กฐ์ ์ธ ๊ฐ๋ฐ์ ํ๊ธฐ ์ํด ์ ์ํ ๋งค์ฐ ๊ฐ๋จํ Framework ์ ๋๋ค.
๋๋ถ๋ถ์ PHP Framework๋ค์ด Modern PHP๋ฅผ ์ํด PHP ํ์ค ๊ถ๊ณ ์(PSR)์ ๋ฐ๋ผ ์ ์๋๊ณ ์์ผ๋ฉฐ PHP์์ ์ง์ํ๋ 7.2 ๋ฒ์ ๋ถํฐ ๊ณต์์ ์ผ๋ก ์ง์ํ๊ณ ์์ต๋๋ค(๊ธ ์์ฑ ์์ ๊ธฐ์ค). ๊ทธ๋ฌ๋ ํ์ฌ PHP๋ก ์ ์๋์ด ์ด์๋๋ ์ฌ์ดํธ ์ผ๋ถ๋ Framework ์์ด ๊ฐ๋ฐ๋์์ผ๋ฉฐ ๊ด๋ฆฌ๊ฐ ๋์ง ์์ ์ ์ง๋ณด์ ๋ฐ ์ถ๊ฐ ๊ฐ๋ฐ์ ์ด๋ ค์์ด ์์ต๋๋ค. ๋ํ ์ด ์์ ์์ ์ฌ์ฉ๋ PHP๋ 5.x ๋ฒ์ ๋๊ฐ ๋๋ถ๋ถ์ด๋ฉฐ ์ด๋ ํ์ฌ Legacy๊ฐ ๋์ด ๋ณด์์ด์, ์ต์ ์ธ์ด ๊ธฐ๋ฅ ๋ฏธ์ง์ ๋ฑ์ผ๋ก ๋ ์ด์ ์ฌ์ฉ์ด ๊ถ๊ณ ๋์ง ์์ต๋๋ค.
๊ฐ์ฅ ์ฌ์ด ํด๊ฒฐ ๋ฐฉ๋ฒ์ PHP 7 ์ด์์ผ๋ก Laravel
๊ณผ ๊ฐ์ Framework๋ฅผ ๋์
ํด ์๋ก ๊ฐ๋ฐํ๋ ๊ฒ์ด๋ ์ด๋ ๋ง์ ์๊ฐ๊ณผ ๋น์ฉ์ ํ์๋ก ํฉ๋๋ค. ์ด์ ๋ณ๋๋ก PHP ๋ฒ์ ์ ์
๊ทธ๋ ์ด๋ ํ๋ ๊ฒ์ ์์ค์ฝ๋์ ์ฌ์ฉ๋ ํ์ฌ Deprecated๋ Feature ๋ค๋ก ์ธํด Side effect๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ์ ์คํ ๊ฒฐ์ ํ ์งํํด์ผ ํฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ๊ฐ์ฅ ํ์ค์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก๋ ๊ธฐ์กด PHP ๋ฒ์ ์ ์ ์งํ ์ฑ ์ ์ง๋ณด์ ๊ณผ์ ์์ MVC ๋์ ํน์ ์ ์ฌํ๊ฒ ๊ตฌํํด ๋๊ฐ์ผ ํ๋ฉฐ ํน์ ์ฌ์ดํธ ํน์ ํ์ด์ง๋ค, ๋๋ ํฐ๋ฆฌ์ Framework๋ฅผ ๋์ ํด ๊ฐ๋ฐ์ ํ๋ฉฐ ์กฐ๊ธ์ฉ ๋ฐ๊พธ์ด ๋๊ฐ๋ ๋ฐฉ๋ฒ์ผ ๊ฒ์ ๋๋ค.
๋ณธ ํ๋ก์ ํธ๋ ์ด๋ฌํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๊ธฐ ์ํด ๊ตฌํ๋ ๋งค์ฐ ๊ฐ๋จํ Framework ์
๋๋ค. Legacy PHP์ธ 5.3.x ์ด์์์ ๋์ํ ์ ์๋๋ก Route Pattern์ ๋์
ํ์์ต๋๋ค. composer
์๋ ํ๊ฒฝ์ ๊ณ ๋ คํ์ฌ composer
์์ด ๋ณธ ํ๋ก์ ํธ๋ฅผ ๋ค์ด๋ก๋ ๋ฐ์ ์ฌ์ฉํ ์ ์๋๋ก ๊ตฌํํ์์ต๋๋ค.
๋ณธ ํ๋ก์ ํธ๋ Apache2 ํ๊ฒฝ์์ ๊ตฌํ ๋ฐ ํ ์คํธ๊ฐ ์ด๋ฃจ์ด์ก์ผ๋ฉฐ ์์ธํ ๋ด์ฉ์ ์๋์ ๊ฐ์ต๋๋ค.
๋ณธ Framework๋ ์๋์ ์๋ฒ ์๊ตฌ์ฌํญ์ ๋ง์กฑํด์ผ ํฉ๋๋ค.
- php 5.3.29 or upper
- apache2 mod_rewrite on
- SimpleXML
๋ณธ Framework๋ composer
๋ฅผ ์ฌ์ฉํ์ง ์๋ PHP ํ๊ฒฝ์ ๊ณ ๋ คํ์์ต๋๋ค. ๋ค์ด๋ก๋ ํ ํ Project root์ ๋ณต์ฌํ์ฌ ์ฌ์ฉํฉ๋๋ค.
APM ํ๊ฒฝ์์ Project root์ ๋ณธ Framework๋ฅผ ์ ์ฅํ์ฌ ์คํํฉ๋๋ค. ๋๋ docker
๊ฐ ์ค์น๋์ด ์์ ๊ฒฝ์ฐ Project root ๋๋ ํฐ๋ฆฌ์์ ์๋์ ๊ฐ์ด docker-compose
๋ช
๋ น์ด๋ฅผ ์ด์ฉํด ์คํํฉ๋๋ค.
docker-compose up
docker-compose ๋ช
๋ น์ด๋ docker ๊ณต์ php:5.3-apache
๋ฅผ ์ด์ฉํ ํ mod_rewrite on์ ํ์ฌ ์ฌ์ฉํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ Project root์ docker-compose.yml
๊ณผ Dockerfile
์ฐธ์กฐ ๋ฐ๋๋๋ค.
๋ณธ Framework์ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ์ต๋๋ค.
- root
- app
- Controllers : Controller ์ ์ฅ ๋๋ ํฐ๋ฆฌ
- Models: Model ์ ์ฅ ๋๋ ํฐ๋ฆฌ (Optional).
- example: ์ฌ์ฉ์(Users) ์ ๋ณด ๋จ์ CRUD ๊ตฌํ Model, View, Controller ์ ์ฅ
- tinypast
- foundation: Framework Core Module
- vendor: ์ธ๋ถ ๋ชจ๋(password_compact, AES ๋ฑ)
- Autoloader.php: namespace autoload (require) ์คํ
- public: Framework ์คํ ํ์ผ(index.php), CSS, JavaScript, font, image ๋ฑ ์ ์ฅ
- resources
- views: View ํ์ผ ์ ์ฅ
- .env.example: .env ํ ํ๋ฆฟ ํ์ผ
- docker-compose.yml: Docker ์คํํ๊ฒฝ ์ ์ฅ
- Dockerfile: Docker ์ด๋ฏธ์ง
- routes.xml: Route ์ ์ฅ xml
- app
๋ณธ Framework์์ ๊ตฌํํ ๊ธฐ๋ฅ์ ์๋์ ๊ฐ์ต๋๋ค. ์ฌ์ฉ ๋ฐฉ๋ฒ์ example์ Users ์์ ์ฐธ์กฐ ๋ฐ๋๋๋ค.
Namespace๋ฅผ ๋์
ํ์์ผ๋ฉฐ Namespace ์ Class ์ด๋ฆ์ผ๋ก require ํ ์ ์์ต๋๋ค ( autoload.php
์ฐธ์กฐ) .
์๋์ ๊ฐ์ด RESTful method๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- GET
- POST
- PUT
- PATCH
- DELETE
PUT, PATCH, DELETE method ์ฌ์ฉ์ ์๋์ ๊ฐ์ด HTML form์ magic method๋ฅผ ๋ช ์ํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค.
<!-- PUT method: ์ฌ์ฉ์ ์ ๋ณด ์์ -->
<form action="/user/1" method="POST">
<input type="hidden" name="_method" value="PUT">
...
</form>
<!-- DELETE method: ์ฌ์ฉ์ ์ ๋ณด ์ญ์ -->
<form action="/user/1" method="POST">
<input type="hidden" name="_method" value="DELETE">
...
</form>
Model View Controller๋ฅผ ์ด์ฉํด ๊ตฌํํ๋ฉฐ Route์ ์ ์ํ์ฌ ์ฌ์ฉํฉ๋๋ค.
Foundation\BaseController
๋ฅผ ์์ํ์ฌ ๊ตฌํํฉ๋๋ค. RESTful Method์ ๋์ํ๋ ๋ฉค๋ฒ ํจ์๋ค์ request ๋ฐฐ์ด์ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ ์ดํ route์ url parameter๋ฅผ ๋ฐ์ต๋๋ค. Foundation\BaseController
๋ View ๋ ๋๋ง & sanitize (๊ณต๋ฐฑ, ํญ ์ ๊ฑฐ / ์ฃผ์์ ๊ฑฐ), ๋ฐฐ์ด ๊ฐ์ XSS filter, redirect ๊ฐ ๊ตฌํ๋์ด ์์ต๋๋ค.
์ฌ์ฉ ์์๋ ์๋์ ๊ฐ์ต๋๋ค.
<?php
namespace App\Controllers;
use Foundation\BaseController;
use App\Models\UserModel;
class UserController extends BaseController
{
private $model = null;
public function __construct()
{
$this->model = new UserModel();
}
// ์ฌ์ฉ์ ๋ชฉ๋ก
public function index($request)
{
$users = $this->model->findAll();
return $this->render('../resources/views/users/index.php', array(
'users' => $users
));
}
// ์ฌ์ฉ์ ์ ๋ณด ์์
public function update($request, $userId)
{
$req = $this->sanitizeRequest($request);
$params = array(
'name' => $req['user_name'],
'email' => $req['user_email'],
'phone' => $req['user_phone'],
'memo' => $req['user_memo']
);
$rows = $this->model->update($params, $userId);
return $this->redirect("/users/{$userId}");
}
}
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฐ์ CRUD ํ๋ฉฐ Foundation\BaseModel
์ ์์๋ฐ์ ๊ตฌํํฉ๋๋ค. Foundation\BaseModel
์๋ ์๋์ ๊ฐ์ ๊ธฐ๋ณธ์ ์ธ CRUD ๊ฐ ๊ตฌํ๋์ด ์์ต๋๋ค.
- findAll(): ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ๋ชจ๋ row๋ฅผ ๊ฐ์ ธ์จ๋ค.
- findById($id): $id์ ํด๋นํ๋ row๋ฅผ ๊ฐ์ ธ์จ๋ค.
- Insert($param): ๋ฐ์ดํฐ ์ฝ์ . ์ฝ์ ํ ๊ฐ์ด ์ ์ฅ๋ $param์ (ํ๋๋ช => ๊ฐ) ์ผ๋ก ๊ตฌ์ฑ๋ ๋ฐฐ์ด์ด ์ ๋ ฅ๋์ด์ผ ํจ.
- Update($param, $id): $id์ ํด๋นํ๋ row์ ๋ฐ์ดํฐ ๊ฐฑ์ . ๊ฐฑ์ ๋ ๊ฐ์ด ์ ์ฅ๋ $param์ (ํ๋๋ช => ๊ฐ) ์ผ๋ก ๊ตฌ์ฑ๋ ๋ฐฐ์ด์ด ์ ๋ ฅ๋์ด์ผ ํจ.
- DeleteById($id): $id์ ํด๋นํ๋ row๋ฅผ ์ญ์ ํ๋ค.
<?php
namespace App\Models;
use Foundation\BaseModel;
class UserModel extends BaseModel
{
/**
* ์์ฑ์
*/
public function __construct()
{
parent::__construct('users');
}
}
Controller์์ ํธ์ถํ render()
ํจ์์ ์ฒซ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ View ํ์ผ(.php)์ 2๋ฒ์งธ ์ธ์์ธ ๋ฐฐ์ด ๊ฐ์ ์ฌ์ฉํ์ฌ ๋ ๋๋งํฉ๋๋ค. ์ฌ์ฉ์ ๋ชฉ๋ก์ ์ถ๋ ฅํ๋ ์์๋ ์๋์ ๊ฐ์ต๋๋ค.
<?php require_once __DIR__ . '/../layouts/header.php'; ?>
<!-- example/resources/views/users/index.php -->
<div class="container">
<h5>์ฌ์ฉ์ ๋ชฉ๋ก</h5>
<div class="list-group">
<?php foreach ($users as $user): ?>
<a href="/users/<?= $user->id ?>" class="list-group-item list-group-item-action">
<div class="row">
<div class="col-1"><?= $user->id ?></div>
<div class="col-2"><?= $user->name ?></div>
<div class="col-3"><?= $user->email ?></div>
<div class="col-3"><?= $user->phone ?></div>
<div class="col-3"><?= $user->created_at ?></div>
</div>
</a>
<?php endforeach; ?>
</div>
</div>
<?php require_once __DIR__ . '/../layouts/footer.php';?>
Route๋ฅผ routes.xm
ํ์ผ์์ ์ ์ํ๋ฉด public/index.php
ํ์ผ์์ routes.xml
ํ์ผ์ Parseํ์ฌ Foundation\Route
ํด๋์ค๊ฐ ์ฌ์ฉํฉ๋๋ค. routes.xml
์ ๊ตฌ์ฑ์ ์๋์ ๊ฐ์ต๋๋ค.
<?xml version="1.0" encoding="UTF-8"?>
<document>
<!-- Route ์ ๋ณด -->
<routes baseUrl="/">
<web>
<route method="GET" url="/" controller="App\Controllers\HomeController@index" />
<route method="GET" url="/info.html" controller="App\Controllers\HomeController@info" />
</web>
</routes>
<!-- Error ๋ฐ์ ์ ์ฒ๋ฆฌํ Handler ์ ๋ณด -->
<errors>
<error code="404" name="Not Found" controller="App\Controllers\ErrorController::notFound" />
<error code="405" name="Method Not Allowed" controller="App\Controllers\ErrorController::methodNotAllowed" />
</errors>
</document>
Route๋ URL์ ์ฌ์ฉ์ ์
๋ ฅ parameter๋ฅผ ๋ฐ์ ์ ์์ผ๋ฉฐ, url์ :value
์ ๊ฐ์ด ์ฝ๋ก (:) ์ผ๋ก ์์ํ๋ ๋ณ์๋ช
์ ์
๋ ฅํ์ฌ ์ฌ์ฉํฉ๋๋ค.
<route method="PUT" url="/users/:userId" controller="App\Controllers\UserController@update" />
์์ route์ :userId
๋ URL์ ์
๋ ฅ๋ ๊ฐ์ด๋ฉฐ(ex. /users/1 ์ 1) controller์์ ํด๋น ๋ณ์๊ฐ์ ์๋์ ๊ฐ์ด ๋ฐ์์ ์ฌ์ฉํฉ๋๋ค.
<?php
class UserController extends BaseController
{
// ์ฌ์ฉ์ ์ ๋ณด ์์ . $userId ๋งค๊ฐ๋ณ์๋ Route url์ ์ ์๋ :userId ํญ๋ชฉ.
public function update($request, $userId)
{
$req = $this->sanitizeRequest($request);
$params = array(
'name' => $req['user_name'],
'email' => $req['user_email'],
'phone' => $req['user_phone'],
'memo' => $req['user_memo']
);
$rows = $this->model->update($params, $userId);
return $this->redirect("/users/{$userId}");
}
}
๋ณธ Framework์๋ 404, 405 Error Handler๊ฐ ์ ์๋์ด ์์ต๋๋ค. routes.xml
์ <errors>
node ์๋ ์๋์ ๊ฐ์ด ์ ์ํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค.
<?xml version="1.0" encoding="UTF-8"?>
<document>
<routes>
<web>...</web>
</routes>
<errors>
<error code="404" name="Not Found" controller="App\Controllers\ErrorController@notFound" />
<error code="405" name="Method Not Allowed" controller="App\Controllers\ErrorController@methodNotAllowed" />
</errors>
</document>
Error Controller๋ ์๋์ ๊ฐ์ด ๊ตฌํํ ์ ์์ต๋๋ค.
<?php
namespace App\Controllers;
use Foundation\BaseController;
class ErrorController extends BaseController
{
/**
* 404 Not found
*
* @param $request $_REQUEST
*/
public function notFound($request)
{
return '404 Not Found';
}
/**
* 405 Method not allowed
*
* @param $request $_REQUEST
*/
public function methodNotAllowed($request)
{
return '405 Method Not Allowed';
}
}
bcrypt, AES๋ฅผ ์ ์ฉํ์์ต๋๋ค.
PHP 5.3.x ๋ฒ์ ์์๋ password_hash, password_verify ํจ์๋ฅผ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์ ์๋์ ๋ชจ๋์ ์ด์ฉํด ํด๋น ํจ์๊ฐ ์์ ๊ฒฝ์ฐ ๋ชจ๋์ ๊ตฌํ๋ ํจ์๋ฅผ ์ฌ์ฉํ๋๋ก ํ์์ต๋๋ค.
openssl ํน์ mcrypt ๊ณ์ด(์ต์ ๋ฒ์ ์์ deprecated)๋ฅผ ์ฌ์ฉํด์ ๊ตฌํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ ๋ ๋ค ์์ ๊ฒฝ์ฐ๋ฅผ ๋๋นํด ์๋์ ์์ AES ๊ตฌํ ๋ชจ๋์ ์ ์ฉํ์์ต๋๋ค.
๋ณธ Framework์ ๊ตฌํ๋์๊ณ ์์ ์ธ ๋ณด์ ๊ด๋ จ๋ ์ฌํญ์ ๋๋ค.
PDO์ Prepared Statement๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
๊ฐ๋ฐ์๊ฐ ์๋์ผ๋ก Controller์์ Sanitize ํ์ฌ ์ฌ์ฉํ ์ ์๋๋ก ํ์์ต๋๋ค. Foundation\BaseController
์ sanitizeStr
, sanitizeRequest
ํจ์๋ฅผ ์ฌ์ฉํด XSS Sanitize ํ ์ ์๋๋ก ํ์์ต๋๋ค.
ํด๋น ๊ธฐ๋ฅ์ ์ธ์ ๊ณผ ๊ฐ์ด ๊ตฌํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ธ์ ๊ตฌํ๊ณผ ๊ฐ์ด ๊ตฌํํ ์์ ์ ๋๋ค.
- Rendering๋ View ํ์ผ์ Sanitize ํ์ฌ ๊ณต๋ฐฑ, ํญ, ์ฃผ์ ๋ฌธ์ ์ ๊ฑฐํ์์ต๋๋ค.
๋ณธ Framework์์๋ ๊ฐ๋จํ ์ฌ์ฉ์ ์ ๋ณด์ CRUD ๊ธฐ๋ฅ์ ํ๋ ์์ ์ฝ๋๋ฅผ ๊ตฌํํ์์ต๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ ์ถ๊ฐ ํexample` ๋๋ ํฐ๋ฆฌ์ ํ์ผ์ ์๋์ ๊ฒฝ๋ก์ ๋ณต์ฌํ์ฌ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
.env.example
ํ์ผ ๋ณต์ฌํ์ฌ.env
ํ์ผ ์์ฑ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค connection ์ ๋ณด ์ ๋ ฅ- test ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ ํ
examples\test.sql
ํ์ผ์ import examples\app\Controllers\UserController.php
ํ์ผ์app\Controllers
์ ๋ณต์ฌexamples\app\Models\UserModel.php
ํ์ผ์app\Models
์ ๋ณต์ฌexamples\resources\views\users
๋ด ๋ชจ๋ ํ์ผ์resources\views\users
์ ๋ณต์ฌroutes.xml.stub
ํ์ผ์ ๋ด์ฉ์routes.xml
ํ์ผ์ ` routes > web node ์ ์ถ๊ฐ/users
URL์ ์ ์ ํ ๋์ ํ์ธ
CSRF ๊ตฌํ์ Session์ด ํ์ํ๋ฉฐ token ๊ฐ ์์ฑ ๊ณผ์ ๊ตฌํ์ ํ์๋ก ํฉ๋๋ค. php 5.3.x ๋ฒ์ ์์ ๋๋คํ token ์ฌ์ฉ์ mcrypt, openssl ์ ๋ณ๋์ ํ๋ฌ๊ทธ์ธ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ํฌํจํด UUID ๋ฑ์ ์ด์ฉํ ๋ฐฉ๋ฒ์ ๊ฒํ ํ ํ ๊ตฌํ์ ์งํํ ์์ ์ ๋๋ค.
- UUID: https://www.php.net/manual/en/function.uniqid.php#94959
- Token ์์ฑ: https://stackoverflow.com/questions/6287903/how-to-properly-add-cross-site-request-forgery-csrf-token-using-php
- Token ๋น๊ต: https://github.com/indigophp/hash-compat
Session์ ํด๋์ค๋ก ๊ตฌํํด ์ ์ฉํ ์์ ์ ๋๋ค. ๊ทธ๋ฌ๋ php 5.3.x์์ SessionHandlerInterface ๋ฅผ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์ ์๋์ ๊ฐ์ ๊ตฌํ์ ์ฐธ์กฐํ ์์ ์ ๋๋ค.
๋ณธ Framework์ Route๋ ์๋์ ๋ด์ฉ์ ์ฐธ์กฐํด ๊ตฌํ๋์์ต๋๋ค.
- https://steampixel.de/en/simple-and-elegant-url-routing-with-php/
- https://stackoverflow.com/questions/11722711/url-routing-regex-php
MIT