Checkpoint
2017-11-25 release
2021-03-12 update
Object-Orientation
parent class abstract |
parent class abstract method |
child class | child class (abstract method) |
---|---|---|---|
not instantiation | only declaration | necessary instantiation | definition |
Application | getRootDir registerRoutes |
MiniBlogApplication | getRootDir registerRoutes |
Controller | AccountController StatusController |
||
DbRepository | UserRepository StatusRepository FollowingRepository |
||
Framework | Mini Blog Application | ||
extends |
e.g. Application.php line 23 etc | |
$this->setDebugMode($debug); | $this = MiniBlogApplication |
e.g. Controller.php line 26 etc | |
$this->controller_name = strtolower(substr(get_class($this), 0, -10)); | $this = AccountController $this = StatusController |
e.g. DbRepository.php line 19 etc | |
$this->setConnection($con); | $this = UserRepository $this = StatusRepository $this = FollowingRepository |
ClassLoader
See Process flow | User registration
method | process |
---|---|
register |
spl_autoload_register(array($this, 'loadClass')); register loadClass in autoload stack |
registerDir |
$this->dirs[0]: C:\XAMPP\xampp_5.6.31\htdocs\mini-blog.localhost/core $this->dirs[1]: C:\XAMPP\xampp_5.6.31\htdocs\mini-blog.localhost/models |
loadClass | read class in core and models (except ClassLoader) |
autoload |
when unloaded class in core and models is new or extends, execute loadClass in autoload stack |
Front controller and .htaccess
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php [QSA,L] </IfModule> |
C:\XAMPP\xampp_5.6.31\htdocs\mini-blog.localhost\web\.htaccess
process: if specified file does not exist, access all index.php
Request
BaseUrl, PATH_INFO
REQUEST_URI | SCRIPT_NAME | BaseUrl | PATH_INFO |
---|---|---|---|
/ | /index.php | / | |
/status/post | /index.php | /status/post | |
/user/user1 | /index.php | /user/user1 | |
/user/user1/status/1 | /index.php | /user/user1/status/1 | |
/account | /index.php | /account | |
/account/signup | /index.php | /account/signup | |
/follow | /index.php | /follow |
REQUEST_URI - BaseUrl = PATH_INFO
IDE (NetBeans IDE 8.2) unused
REQUEST_URI | SCRIPT_NAME | BaseUrl | PATH_INFO |
---|---|---|---|
/index.php/ | /index.php | /index.php | / |
/index.php/status/post | /index.php | /index.php | /status/post |
/index.php/user/user1 | /index.php | /index.php | /user/user1 |
/index.php/user/user1/status/1 | /index.php | /index.php | /user/user1/status/1 |
/index.php/account | /index.php | /index.php | /account |
/index.php/account/signup | /index.php | /index.php | /account/signup |
/index.php/follow | /index.php | /index.php | /follow |
REQUEST_URI - BaseUrl = PATH_INFO
IDE (NetBeans IDE 8.2) unused
$_POST (getPost)
key | value (example) |
---|---|
['_token'] | 'ed38d8176bff02c2b0364055da5e8cb4d72fc85d' |
<input type="hidden" name="_token" value="<?php echo $this->escape($_token); ?>" />
Router
method | process |
---|---|
__construct |
__construct($definitions) $definitions = $this->registerRoutes() reference Application.php line 54 (new Router($this->registerRoutes())) reference MiniBlogApplication.php line 17 (protected function registerRoutes()) |
compileRoutes | create $this->routes↓ from Routing definition↓ |
resolve | search $this->routes↓ and specify controller and action from PATH_INFO |
PATH_INFO | controller | action |
---|---|---|
/ | status | index |
/status/post | status | post |
/user/:user_name | status | user |
/user/:user_name/status/:id | status | show |
/account | account | index |
/account/:action | account | |
/follow | account | follow |
pattern | $params['controller'] | $params['action'] |
---|---|---|
/ | status | index |
/status/post | status | post |
/user/(?P<user_name>[^/]+) | status | user |
/user/(?P<user_name>[^/]+)/status/(?P<id>[^/]+) | status | show |
/account | account | index |
/account/(?P<action>[^/]+) | account | Note |
/follow | account | follow |
Note identify action
(Router.php line 62) preg_match('#^' . $pattern . '$#', $path_info, $matches)
e.g. set $matches['action']=>'signup'↓ from /account/signup
named subpattern: (?P<user_name>[^/]+) etc
$path_info | $matches ($params) |
---|---|
/user/user1 /user/user2 /user/user3 |
(except controller, action) $matches['user_name']=>'user1' $matches['user_name']=>'user2' $matches['user_name']=>'user3' |
/user/user1/status/1 /user/user1/status/2 /user/user2/status/3 /user/user2/status/4 /user/user3/status/5 |
(except controller, action) $matches['user_name']=>'user1', $matches['id']=>'1' $matches['user_name']=>'user1', $matches['id']=>'2' $matches['user_name']=>'user2', $matches['id']=>'3' $matches['user_name']=>'user2', $matches['id']=>'4' $matches['user_name']=>'user3', $matches['id']=>'5' |
/account/signup /account/register /account/signin /account/authenticate /account/signout |
$matches['action']=>'signup' $matches['action']=>'register' $matches['action']=>'signin' $matches['action']=>'authenticate' $matches['action']=>'signout' |
regular expression (named subpattern) | matched string |
---|---|
/user/(?P<user_name>[^/]+) |
/user/user1 /user/user2 /user/user3 |
/user/(?P<user_name>[^/]+)/status/(?P<id>[^/]+) |
/user/user1/status/1 /user/user1/status/2 /user/user2/status/3 /user/user2/status/4 /user/user3/status/5 |
/account/(?P<action>[^/]+) |
/account/signup /account/register /account/signin /account/authenticate /account/signout |
Response
property | Application.php (runAction) |
---|---|
$content | response->setContent($content) |
DbManager
$name | PDO instance | Note | Note |
---|---|---|---|
master | $con = new PDO | dsn | mysql:dbname=mini_blog;host=localhost;charset=utf8 |
user | root | ||
password | |||
options |
set connection information (MiniBlogApplication.php line 39, DbManager.php line 20)
Note information to pass to constructor of PDO class
key ($name) | value |
---|---|
master | $con (PDO instance) |
key ($repository_name) | value ($name) |
---|---|
Status | master |
User | master |
Following | master |
setRepositoryConnectionMap($repository_name, $name)
key ($repository_name) | value ($repository) | method (DbManager) |
---|---|---|
User | UserRepository instance | db_manager->get('User') |
Status | StatusRepository instance | db_manager->get('Status') |
Following | FollowingRepository instance | db_manager->get('Following') |
DbRepository
table | class |
---|---|
user table | UserRepository |
status table | StatusRepository |
following table | FollowingRepository |
property | setConnection($con) | DbManager |
---|---|---|
$con |
UserRepository->con StatusRepository->con FollowingRepository->con |
DbManager->connections['master'] = $con (PDO instance) |
method | method implementation |
---|---|
execute($sql, $params = array()) | $stmt = $this->con->prepare($sql); $stmt->execute($params); return $stmt; ($stmt: PDOstatement instance) |
fetch($sql, $params = array()) | return $this->execute($sql, $params)->fetch(PDO::FETCH_ASSOC); |
fetchAll($sql, $params = array()) | return $this->execute($sql, $params)->fetchAll(PDO::FETCH_ASSOC); |
prepare (escape values in placeholders)
PDO::FETCH_ASSOC (receive result as associative arrays)
Session
static property | process |
---|---|
static $sessionStarted self::$sessionStarted |
check that session_start() is not called more than once |
static $sessionIdRegenerated self::$sessionIdRegenerated |
check that session_regenerate_id(true) is not called more than once |
it can be used without being instantiated (variable's value is retained even if process moves outside function)
$_SESSION
key | value |
---|---|
['user']['id'] | '1' |
['user']['user_name'] | 'user1' |
['user']['password'] | 'ee5281d035bd1bd7786301be4274a68b006ae916' |
['user']['created_at'] | 2017-11-01 00:00:00 |
session->get('user')
session->set('user', $user)
Login User ID: user1, Password: password
See Preparation | Test Data
key | value |
---|---|
['_authenticated'] | true(Login) or false(Not Login) |
session->setAuthenticated()
session->isAuthenticated()
key | value (example) |
---|---|
['csrf_tokens/account/signin'][0] ... | 'ed38d8176bff02c2b0364055da5e8cb4d72fc85d' |
['csrf_tokens/account/signin'][9] | 'bfb55f8dd338b02efea1ad1a12c249b722b499ae' |
['csrf_tokens/account/signup'][0] ... | '117d0e6d9da8ec9e595110d7e517993901ee77ce' |
['csrf_tokens/account/signup'][9] | '17fbad25e479cdc7e712b9977d2b9d9fdb85ecb3' |
['csrf_tokens/account/follow'][0] ... | '4b710f10e11206dd0e5ba2b23fea9c993b20a6c3' |
['csrf_tokens/account/follow'][9] | '77e5617a7d149f6f4774f4f607affaa3a171cdc9' |
['csrf_tokens/status/post'][0] ... | '62138aa5b21213242eee2135892b9589609cc9d6' |
['csrf_tokens/status/post'][9] | '27da715c32fe321f52f20ddf0a45e462a611edf3' |
method | process |
---|---|
session->get('csrf_tokens/account/signin', array()) | $_SESSION['csrf_tokens/account/signin'] --> $tokens |
session->set('csrf_tokens/account/signin', $tokens) | $tokens --> $_SESSION['csrf_tokens/account/signin'] |
Application
PHP script | require_once/require | |
---|---|---|
/controllers | require_once |
Application.php (line 231) findController require_once doesn't read a file that has been read once (it prevents function redefinition and variable reassignment) Mini Blog Application AccountController.php StatusController.php |
/core | require |
ClassLoader.php (line 40) loadClass Framework |
/models | require |
ClassLoader.php (line 40) loadClass get use DbManager->repositories[] (DbManager.php get) it creates an instance only first time same effect as require_once (it prevents function redefinition and variable reassignment) Mini Blog Application UserRepository.php StatusRepository.php FollowingRepository.php |
property | instance |
---|---|
MiniBlogApplication->request | Request |
MiniBlogApplication->response | Response |
MiniBlogApplication->session | Session |
MiniBlogApplication->db_manager | DbManager |
MiniBlogApplication->router | Router |
Controller
function | return value |
---|---|
strtolower(substr(get_class($this), 0, -10)) | 'user' ($this->controller_name) |
get_class($this) (e.g. $this: UserController) | 'UserController' |
substr('UserController', 0, -10) | 'User' (Controller: 10 characters) |
strtolower('User') | 'user' |
$this | return value |
---|---|
'AccountController' | 'account' |
'StatusController' | 'status' |
property | property (Application) | instance |
---|---|---|
AccountController->request | MiniBlogApplication->request | Request |
AccountController->response | MiniBlogApplication->response | Response |
AccountController->session | MiniBlogApplication->session | Session |
AccountController->db_manager | MiniBlogApplication->db_manager | DbManager |
StatusController->request | MiniBlogApplication->request | Request |
StatusController->response | MiniBlogApplication->response | Response |
StatusController->session | MiniBlogApplication->session | Session |
StatusController->db_manager | MiniBlogApplication->db_manager | DbManager |
method | process |
---|---|
generateCsrfToken('account/signin') |
$tokens Max 10 generate $token (SHA-1 hash value), set $token in $tokens return $token '_token' => $this->generateCsrfToken('account/signin') |
checkCsrfToken('account/signin', $token) |
true: if there is $token in $tokens, delete $token false: None |
value | |
---|---|
AccountController | array('index', 'signout', 'follow') |
StatusController | array('index', 'post') |
- | true (All actions required login) unused |
View
HTML structure
Account Registration (User Registration)
layout.php (common with all screens)
render execution3, output3 (View::render in View::render), $_layout = false
$base_url
$session
$title
$_content
signup.php
render execution1, output2 (View::render in Controller::render), $_layout = 'layout'
$base_url
$user_name
$password
$_token
inputs.php
render execution2, output1 (View::render in signup.php), $_layout = false
$user_name
$password
Error screen
errors.php (common with all error screens)
inputs - signup - layout comparison
output buffering (function to buffer output information internally)
function | process |
---|---|
ob_start() | start output buffering |
ob_implicit_flush(0) | disable automatic flashing of buffer (finally buffer is output) |
ob_get_clean | get contents of current buffer and delete output buffer |
render
View->base_dir | C:\XAMPP\xampp_5.6.31\htdocs\mini-blog.localhost/views |
View->defaults['request'] | Request |
View->defaults['base_url'] | '' |
View->defaults['session'] | Session |
ccount Registration (User Registration)
array_merge | extract | require | inputs.php |
---|---|---|---|
View->defaults['request'] | $request | ||
View->defaults['base_url'] | $base_url | ||
View->defaults['session'] | $session | ||
$_variables['user_name'] | $user_name | $user_name | '' |
$_variables['password'] | $password | $password | '' |
$_variables['_token'] | $_token |
$content = ob_get_clean() ... inputs.php
array_merge | extract | require | signup.php |
---|---|---|---|
View->defaults['request'] | $request | ||
View->defaults['base_url'] | $base_url | $base_url | '' |
View->defaults['session'] | $session | ||
$_variables['user_name'] | $user_name | $user_name | '' |
$_variables['password'] | $password | $password | '' |
$_variables['_token'] | $_token | $_token | e.g. 'e1968c9ccc99acfccadd1ff7a524f1eb74c18ec1' |
$content = ob_get_clean() ... signup.php + inputs.php
array_merge | extract | require | layout.php |
---|---|---|---|
View->defaults['request'] | $request | ||
View->defaults['base_url'] | $base_url | $base_url | '' |
View->defaults['session'] | $session | $session | Session |
$_variables['title'] | $title | $title | 'Account Registration' |
$_variables['_content'] | $_content | $_content | signup.php + inputs.php |
echo $_content
$content = ob_get_clean() ... layout.php + signup.php + inputs.php
Homepage
array_merge | extract | require | status.php |
---|---|---|---|
View->defaults['request'] | $request | ||
View->defaults['base_url'] | $base_url | $base_url | '' |
View->defaults['session'] | $session | ||
$_variables['status']['id'] | $status['id'] | $status['id'] | '1' |
$_variables['status']['user_id'] | $status['user_id'] | ||
$_variables['status']['body'] | $status['body'] | $status['body'] | 'status1 user1 test1' |
$_variables['status']['created_at'] | $status['created_at'] | $status['created_at'] | '2017-11-01 00:00:00' |
$_variables['status']['user_name'] | $status['user_name'] | $status['user_name'] | 'user1' |
foreach (Contribution status1-status4)
$content = ob_get_clean() ... status.php
array_merge | extract | require | index.php |
---|---|---|---|
View->defaults['request'] | $request | ||
View->defaults['base_url'] | $base_url | $base_url | '' |
View->defaults['session'] | $session | ||
$_variables['statuses'] | $statuses | $statuses | Contribution status1-status4 4 cases |
$_variables['body'] | $body | $body | '' |
$_variables['_token'] | $_token | $_token | e.g. 'e1968c9ccc99acfccadd1ff7a524f1eb74c18ec1' |
foreach (Contribution status1-status4)
$content = ob_get_clean() ... index.php + status.php
array_merge | extract | require | layout.php |
---|---|---|---|
View->defaults['request'] | $request | ||
View->defaults['base_url'] | $base_url | $base_url | '' |
View->defaults['session'] | $session | $session | Session |
$_variables['title'] | $title | $title | 'Home' |
$_variables['_content'] | $_content | $_content | index.php + status.php |
foreach (Contribution status1-status4)
echo $_content
$content = ob_get_clean() ... layout.php + index.php + status.php
Error screen
controller | action | controller->render ($template) (input screen) |
---|---|---|
AccountController | registerAction | signup |
authenticateAction | signin | |
StatusController | postAction | index |
Exception
HttpNotFoundException (Error)
HttpNotFoundException.php
<?php class
HttpNotFoundExceptionextends
Exception{};
Application.php run (throw) No route found
e.g. No route found for /account
177 if ($params === false) { 178
thrownew
HttpNotFoundException('No route found for ' . $this->request->getPathInfo()); 179 }
Application.php runAction (throw) controller is not found
e.g. AccountController controller is not found.
209 if ($controller === false) { 210
thrownew
HttpNotFoundException($controller_class . ' controller is not found.'); 211 }
Controller.php forward404 (throw) Error
e.g. Forwarded 404 page from account/register
94 protected function
forward404()95 { 96
thrownew
HttpNotFoundException('Forwarded 404 page from ' 97 . $this->controller_name . '/' . $this->action_name); 98 }
class | method | check | error |
---|---|---|---|
Controller | run |
if (!method_exists($this, $action_method)) { $this->forward404(); } |
No class method |
AccountController | registerAction authenticateAction followAction |
if (!$this->request->isPost()) { $this->forward404(); } |
Not POST |
followAction |
if (!$following_name) { $this->forward404(); } |
No following name | |
if (!$follow_user) { $this->forward404(); } |
No follow user | ||
StatusController | postAction |
if (!$this->request->isPost()) { $this->forward404(); } |
Not POST |
userAction |
if (!$user) { $this->forward404(); } |
No user | |
showAction |
if (!$status) { $this->forward404(); } |
No status |
Application.php run (catch) Error display on browser
185 }
catch(
HttpNotFoundException$e) { 186 $this->
render404Page($e);
Note NetBeans Debugging methods (Change Variable value to change Process flow) See
UnauthorizedActionException (Transition to Login)
UnauthorizedActionException.php
<?php class
UnauthorizedActionExceptionextends
Exception{};
Controller.php run (throw) Login Necessary && Not Login (Deny false → true)
53 if ($this->needsAuthentication($action) && !$this->session->isAuthenticated()) { 54
thrownew
UnauthorizedActionException(); 55 }
Application.php run (catch) Login screen
runAction('account', 'signin')
187 }
catch(
UnauthorizedActionException$e) { 188 list($controller, $action) = $this->
login_action; 189 $this->runAction($controller, $action); 190 }
Note Process flow (Account information management and Login) See
Mini Blog Application
function | class | method | |
---|---|---|---|
Check | Controller | checkCsrfToken($form_name, $token) | Token check |
Request | isPost() | POST check | |
Session | isAuthenticated() | Login status check | |
FollowingRepository | isFollowing($user_id, $following_id) | true (1, 2) false (1, 3) Note !isFollowing null (1, 1) |
|
UserRepository | isUniqueUserName($user_name) | user name duplication check |
|
Data set | Controller | generateCsrfToken($form_name) | generate Token |
Session | clear() | Logout | |
Session | set($name, $value) | Login user ('user', $user) |
|
Session | setAuthenticated($bool) | Login status true, false | |
Data get | DbManager | get($repository_name) | 'Following' FollowingRepository 'Status' StatusRepository 'User' UserRepository |
Request | getPost($name) | 'user_name' 'password' '_token' 'body' 'following_name' |
|
Session | get($name) | Login user ('user') | |
StatusRepository | fetchAllByUserId($user_id) | status1-status2 status3-status4 status5 |
|
StatusRepository | fetchAllPersonalArchivesByUserId($user_id) | 'user1' (2 cases), 'user2' (2 cases) |
|
StatusRepository | fetchByIdAndUserName($id, $user_name) | status1 | |
UserRepository | fetchAllFollowingsByUserId($user_id) | Following 'user2' | |
UserRepository | fetchByUserName($user_name) | 'user1' 1record 'user2' 1record 'user3' 1record 'user4' 1record |
|
UserRepository | hashPassword($password) | 'password' | |
Data output | FollowingRepository | insert($user_id, $following_id) | 1, 3 |
StatusRepository | insert($user_id, $body) | 1, 'status6 user1 test6' | |
UserRepository | insert($user_name, $password) | 'user4', 'password' | |
Screen | Controller | forward404() | |
Controller | redirect($url) | ||
Controller | render($variables = array(), $template = null, $layout = 'layout') |