├── README.md ├── app ├── .htaccess ├── backend │ ├── auth │ │ ├── config.php │ │ ├── cookie.php │ │ ├── delete-account.php │ │ ├── login.php │ │ ├── logout.php │ │ ├── profile.php │ │ ├── register.php │ │ ├── update-account.php │ │ └── user.php │ ├── classes │ │ ├── Token.php │ │ ├── User.php │ │ └── Validation.php │ └── core │ │ ├── Config.php │ │ ├── Cookie.php │ │ ├── Database.php │ │ ├── Hash.php │ │ ├── Helpers.php │ │ ├── Init.php │ │ ├── Input.php │ │ ├── Password.php │ │ ├── Redirect.php │ │ └── Session.php ├── frontend │ ├── assets │ │ ├── css │ │ │ ├── login-form.css │ │ │ └── profile.css │ │ └── js │ │ │ ├── login-form.js │ │ │ └── profile.js │ ├── includes │ │ ├── errors │ │ │ └── 404.php │ │ ├── footer.php │ │ ├── header.php │ │ ├── messages.php │ │ └── navbar.php │ └── pages │ │ ├── home.php │ │ ├── login.php │ │ ├── profile.php │ │ ├── register.php │ │ └── update-account.php └── setup │ └── php_boilerplate.sql ├── delete-account.php ├── index.php ├── login.php ├── logout.php ├── profile.php ├── register.php ├── start.php ├── test.php └── update-account.php /README.md: -------------------------------------------------------------------------------- 1 | # Simple PHP Boilerplate 2 | - This project only exists for helping my colleagues in school. 3 | 4 | ## Table of Contents 5 | 6 | - [Concepts](#concepts-) 7 | - [Files Controller](#files-controller) 8 | - [Seperation of Ideas](#seperation-of-ideas) 9 | - [Level of Useability](#level-of-useability) 10 | - [Organize the Files](#organize-the-files) 11 | - [Advantages](#advantages-) 12 | - [Knowledge Requirements](#knowledge-requirements-) 13 | - [Features](#features-) 14 | - [Getting Started](#getting-started) 15 | - [Overview](#overview) 16 | - [Nice Configurations](#nice-configurations) 17 | - [Easy Validation](#easy-validation) 18 | - [CSRF Protection](#csrf-protection) 19 | - [Secure Password](#secure-password) 20 | - [Sql Injection Protection](#sql-injection-protection) 21 | - [Core Classes](#core-classes-) 22 | - [Cookie Class](#cookie-class) 23 | - [Database Class](#database-class) 24 | - [Hash Class](#hash-class) 25 | - [Helper Functions](#helper-functions) 26 | - [Input Class](#input-class) 27 | - [Password Class](#password-class) 28 | - [Redirect Class](#redirect-class) 29 | - [Session Class](#session-class) 30 | - [Helpful Classes](#helpful-classes-) 31 | - [Token Class](#token-class) 32 | - [User Class](#user-class) 33 | - [Validation Class](#validation-class) 34 | 35 | ### Concepts : 36 | When you use this app the first things you'll notice are the files and folder in the directory. The app folder composed of frontend and backend folders, which separates the front design and backend logic. The files that you saw in the outside of the app folder, I think as the controllers, think this as you play a jigsaw puzzle, you need to connect the different pieces to solve it or form it. So.. as you see the content of the files, there is no logic or designs include on it, except the require once function that use to insert or connect the files. Those require name defined in the 'start.php', to make it easier to memorize, act as an alias or be constant for the file locations. 37 | 38 | To understand the structure and idea of this app, think in this way; 39 | 40 | ##### Files Controller 41 | The files that outside in the app folders are puzzles that you need to solve. Then the files inside in the app folder are the pieces of a puzzle that you need to require or connect. 42 | 43 | ##### Separation of Ideas 44 | The app folder contains two folders that are frontend and backend folder. Think of it as the separation of logic and design in your application. 45 | 46 | ##### Level of Usability 47 | The backend folder contains auth, classes, core folders. The lower the level, the low chance that you will use in your logic. Auth is level 2, Classes is level 1, and Core is level 0. So in Auth folder, where you will put your logic that connects from your frontend pages. Then the classes folders are your objects or tools that logic behind in Auth files. Then the Core folder is the abstract ideas behind your classes folder. And that's it. 48 | 49 | ##### Organize the Files 50 | The Frontend folder contains assets, includes, and pages folders which were chopped to reuse your design and codes. Like assets folder where the css, js, images lived, includes folder where the headers, navbar, messages, footer are separated and the pages folder where you all your application pages lived. 51 | 52 | 53 | 54 | ### Advantages : 55 | 56 | - Organize File Structure 57 | - High Security 58 | - Code Reuseable 59 | 60 | ### Knowledge Requirements : 61 | 62 | - [PHP Programming Basics](https://www.youtube.com/watch?v=XKWqdp17BFo&list=PLfdtiltiRHWHjTPiFDRdTOPtSyYfz3iLW) 63 | - [Good Understanding in OOP](https://www.youtube.com/watch?v=ipp4WPDwwvk&list=PLfdtiltiRHWF0RicJb20da8nECQ1jFvla) (optional) 64 | - Open Minded 65 | 66 | ### Features : 67 | 68 | - Nice Configurations 69 | - Easy Validation 70 | - CSRF Protection 71 | - Secure Password 72 | - Sql Injection Protection 73 | - A lot of helpful classes. 74 | 75 | ### Getting Started 76 | 77 | #### [Download ZIP](https://github.com/jandaryl/simple-php-boilerplate/archive/master.zip) or Git Clone 78 | ```php 79 | git clone https://github.com/jandaryl/simple-php-boilerplate.git 80 | ``` 81 | 82 | ### Setup environment 83 | 84 | Follow the steps below: 85 | 86 | 1. [Download and install Xampp](https://www.apachefriends.org/download.html), which is used to manage the system. 87 | 2. Move the files to the htdocs folder of Xampp. 88 | 3. Import the database and set the configuration. 89 | 90 | ### Overview 91 | 92 | #### Nice Configurations 93 | 94 | ``` php 95 | array( 100 | 'name' => 'AppName', 101 | ), 102 | 'mysql' => array( 103 | 'host' => '127.0.0.1', 104 | 'username' => 'root', 105 | 'password' => '', 106 | 'db_name' => 'php_boilerplate' 107 | ), 108 | ... 109 | 110 | check($_POST, array( 131 | 'username' => array( 132 | 'required' => true, 133 | 'min' => 2, 134 | 'unique' => 'users' 135 | ), 136 | 'current_password' => array( 137 | 'required' => true, 138 | 'min' => 6, 139 | 'verify' => 'password' 140 | ), 141 | 'new_password' => array( 142 | 'optional' => true, 143 | 'min' => 6, 144 | 'bind' => 'confirm_new_password' 145 | ) 146 | 'confirm_new_password' => array( 147 | 'optional' => true, 148 | 'min' => 6, 149 | 'match' => 'new_password', 150 | 'bind' => 'new_password', 151 | ), 152 | )); 153 | 154 | if ($validate->passed() { 155 | // Awesome Logic... 156 | } else { 157 | echo $validate->$error(); // First error message. 158 | 159 | foreach ($validate->errors() as $error) // All error messages. 160 | { 161 | echo $error; 162 | } 163 | } 164 | ... 165 | ``` 166 | 167 | ### CSRF Protection 168 | 169 | ```php 170 | "> 176 | 177 | ... 178 | 179 | array( 197 | 'algo_name' => PASSWORD_DEFAULT, 198 | 'cost' => 10, 199 | 'salt' => 50, 200 | ), 201 | ... 202 | 203 | create(array( 208 | ... 209 | 'password' => Password::hash(Input::get('password')), 210 | ... 211 | )); 212 | ... 213 | 214 | _user->data()->password)) { 219 | // Do awesome stuff... 220 | } 221 | ... 222 | ``` 223 | 224 | ### Sql Injection Protection 225 | ```php 226 | _pdo = new PDO('mysql:host='.Config::get('mysql/host')...); 230 | ... 231 | $this->_query->bindvalue($x, $param); 232 | ... 233 | $this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ); 234 | ... 235 | 236 | ``` 237 | 238 | ### Core Classes : 239 | 240 | #### Cookie Class 241 | ```php 242 | get($table, $where = array()) 267 | $database->get('users', array('id', '=', '13'); 268 | 269 | // Insert the data to database. 270 | // $database->insert($table, $fields = array()) 271 | $database->insert('users', array( 272 | 'username' => 'johnny123', 273 | 'password' => Password::hash('secret'), 274 | 'name' => 'JohnDoe', 275 | 'joined' => date('Y-m-d H:i:s') 276 | )); 277 | 278 | // Update the data from database. 279 | // $database->update($table, $id, $fields = array()) 280 | $database->update('users', '13', array( 281 | 'username' => 'newusernname', 282 | 'password' => Password::hash('newsecret'), 283 | 'name' => 'newname', 284 | )); 285 | 286 | // Delete the data from database. 287 | // $database->delete($table, $where = array()) 288 | $database->delete('users', array('id', '=', '13'); 289 | 290 | // Get the first data from database. 291 | $database->first(); 292 | 293 | // Count the data from database. 294 | $database->count(); 295 | 296 | // Return a results of object data from database. 297 | $database->results(); 298 | 299 | // Get error from query. 300 | $database->error(); 301 | 302 | ``` 303 | #### Hash Class 304 | ```php 305 | create(array( 426 | 'username' => Input::get('username'), 427 | 'password' => Password::hash(Input::get('password')), 428 | 'name' => Input::get('name'), 429 | 'joined' => date('Y-m-d H:i:s'), 430 | )); 431 | 432 | // Update the user data. 433 | $user->update(array( 434 | 'username' => Input::get('username'), 435 | 'password' => Password::hash(Input::get('password')), 436 | 'name' => Input::get('name'), 437 | 'joined' => date('Y-m-d H:i:s'), 438 | ), '13'); 439 | 440 | // Find the user if exists. 441 | $user->find('13'); // return true or false 442 | $user->find('johnny123'); // return true or false 443 | 444 | // Login the user. 445 | // The 3rd argument is to check if the user want to remember. 446 | $user->login('johnny123', 'secret', true); 447 | 448 | // Check the user if exists. 449 | $user->exists(); 450 | 451 | // Logout the user. 452 | // Delete the session and cookie that attached. 453 | $user->logout(); 454 | 455 | 456 | // Get the user data in object way. 457 | $user->data(); 458 | $user->data()->username; // 'johnny123' 459 | $user->data()->name; // 'JohnDoe' 460 | 461 | // Check if the user logged in. 462 | $user->isLoggedIn(); // Return true or false. 463 | 464 | // Delete the data from database. 465 | $user->deleteMe(); 466 | 467 | ``` 468 | #### Validation Class 469 | ```php 470 | true, 'can be null or no value' 478 | // required => true, 'cannot be null or no value' 479 | // bind => 'input_name', 'make connect to other input' 480 | // min => 'any_number', 'minimum length of that input' 481 | // max => 'any_number', 'maximum length of that input' 482 | // match => 'input_name', 'check if match input from another' 483 | // email => true, 'check the input if valid email' 484 | // alnum => true, 'check if the input alpha numeric' 485 | // unique => 'table_name', 'check if the input unique from table' 486 | // verify => 'password', 'only in password, check the current' 487 | 488 | $validation = $validate->check($_POST, array( 489 | 'username' => array( 490 | 'required' => true, 491 | 'unique' => 'users', 492 | 'min' => 2, 493 | 'max' => 20, 494 | 'matches' => 'password', 495 | alnum => true 496 | ), 497 | )); 498 | 499 | // Check if there is an errors. 500 | if ($validate->passed() { 501 | // Query to the database... 502 | } else { 503 | // For first error message. 504 | // echo $validate->$error(); 505 | 506 | // For all error messages. 507 | foreach ($validate->errors() as $error) 508 | { 509 | echo $error; 510 | } 511 | } 512 | 513 | ``` 514 | 515 | Thanks for reading! I hope this is useful... 516 | 517 | ### License 518 | 519 | Simple PHP Boilerplate is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 520 | 521 | 522 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | Options --Indexes 2 | 3 | -------------------------------------------------------------------------------- /app/backend/auth/config.php: -------------------------------------------------------------------------------- 1 | array( 6 | 'name' => 'AppName', 7 | ), 8 | 9 | 'mysql' => array( 10 | 'host' => '127.0.0.1', 11 | 'username' => 'root', 12 | 'password' => '', 13 | 'db_name' => 'php_boilerplate' 14 | ), 15 | 16 | 'password' => array( 17 | 'algo_name' => PASSWORD_DEFAULT, 18 | 'cost' => 10, 19 | 'salt' => 50, 20 | ), 21 | 22 | 'hash' => array( 23 | 'algo_name' => 'sha512', 24 | 'salt' => 30, 25 | ), 26 | 27 | 'remember' => array( 28 | 'cookie_name' => 'token', 29 | 'cookie_expiry' => 604800 30 | ), 31 | 32 | 'session' => array( 33 | 'session_name' => 'user', 34 | 'token_name' => 'csrf_token' 35 | ), 36 | ); 37 | -------------------------------------------------------------------------------- /app/backend/auth/cookie.php: -------------------------------------------------------------------------------- 1 | get('users_session', array('hash', '=', $hash)); 8 | 9 | if($hashCheck->count()) 10 | { 11 | $user = new User($hashCheck->first()->user_id); 12 | $user->login(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/backend/auth/delete-account.php: -------------------------------------------------------------------------------- 1 | deleteMe(); 5 | 6 | Redirect::to('index.php'); 7 | -------------------------------------------------------------------------------- /app/backend/auth/login.php: -------------------------------------------------------------------------------- 1 | check($_POST, array( 11 | 'username' => array( 12 | 'required' => true, 13 | ), 14 | 15 | 'password' => array( 16 | 'required' => true 17 | ), 18 | )); 19 | 20 | if ($validation->passed()) 21 | { 22 | $remember = (Input::get('remember') === 'on') ? true : false; 23 | $login = $user->login(Input::get('username'), Input::get('password'), $remember); 24 | 25 | if ($login) 26 | { 27 | Redirect::to('index.php'); 28 | } 29 | else 30 | { 31 | echo '
Incorrect Credentials! Please try again...
'; 32 | } 33 | } 34 | else 35 | { 36 | foreach ($validation->errors() as $error) 37 | { 38 | echo '
' . cleaner($error) . '
'; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/backend/auth/logout.php: -------------------------------------------------------------------------------- 1 | logout(); 5 | 6 | Redirect::to('index.php'); 7 | -------------------------------------------------------------------------------- /app/backend/auth/profile.php: -------------------------------------------------------------------------------- 1 | isLoggedIn()) 5 | { 6 | Redirect::to('index.php'); 7 | } 8 | 9 | $data = $user->data(); 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/backend/auth/register.php: -------------------------------------------------------------------------------- 1 | check($_POST, array( 11 | 'name' => array( 12 | 'required' => true, 13 | 'min' => 2, 14 | 'max' => 50 15 | ), 16 | 17 | 'username' => array( 18 | 'required' => true, 19 | 'min' => 2, 20 | 'max' => 20, 21 | 'unique' => 'users' 22 | ), 23 | 24 | 'password' => array( 25 | 'required' => true, 26 | 'min' => 6 27 | ), 28 | 29 | 'password_again' => array( 30 | 'required' => true, 31 | 'matches' => 'password' 32 | ) 33 | )); 34 | 35 | if ($validate->passed()) 36 | { 37 | try 38 | { 39 | $user->create(array( 40 | 'username' => Input::get('username'), 41 | 'password' => Password::hash(Input::get('password')), 42 | 'name' => Input::get('name'), 43 | 'joined' => date('Y-m-d H:i:s'), 44 | 'groups' => 1 45 | )); 46 | 47 | Session::flash('register-success', 'Thanks for registering! You can login now.'); 48 | Redirect::to('index.php'); 49 | } 50 | catch(Exception $e) 51 | { 52 | die($e->getMessage()); 53 | } 54 | } 55 | else 56 | { 57 | foreach ($validate->errors() as $error) 58 | { 59 | echo '
' . cleaner($error) . '
'; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/backend/auth/update-account.php: -------------------------------------------------------------------------------- 1 | check($_POST, array( 11 | 'name' => array( 12 | 'required' => true, 13 | 'min' => 2, 14 | 'max' => 50 15 | ), 16 | 'username' => array( 17 | 'required' => true, 18 | 'min' => 2, 19 | 'max' => 20 20 | ), 21 | 'current_password' => array( 22 | 'required' => true, 23 | 'min' => 6, 24 | 'verify' => 'password' 25 | ), 26 | 'new_password' => array( 27 | 'optional' => true, 28 | 'min' => 6, 29 | 'bind' => 'confirm_new_password' 30 | ), 31 | 32 | 'confirm_new_password' => array( 33 | 'optional' => true, 34 | 'min' => 6, 35 | 'match' => 'new_password', 36 | 'bind' => 'new_password', 37 | ), 38 | )); 39 | 40 | if ($validation->passed()) 41 | { 42 | try 43 | { 44 | $user->update(array( 45 | 'name' => Input::get('name'), 46 | 'username' => Input::get('username'), 47 | )); 48 | 49 | if ($validation->optional()) 50 | { 51 | $user->update(array( 52 | 'password' => Password::hash(Input::get('new_password')) 53 | )); 54 | } 55 | Session::flash('update-success', 'Profile successfully updated!'); 56 | Redirect::to('index.php'); 57 | } 58 | catch(Exception $e) 59 | { 60 | die($e->getMessage()); 61 | } 62 | } 63 | 64 | else 65 | { 66 | echo '
' . cleaner($validation->error()) . '
'; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/backend/auth/user.php: -------------------------------------------------------------------------------- 1 | _db = Database::getInstance(); 14 | 15 | $this->_sessionName = Config::get('session/session_name'); 16 | 17 | $this->_cookieName = Config::get('remember/cookie_name'); 18 | 19 | if (! $user) 20 | { 21 | if (Session::exists($this->_sessionName)) 22 | { 23 | $user = Session::get($this->_sessionName); 24 | 25 | if ($this->find($user)) 26 | { 27 | $this->_isLoggedIn = true; 28 | } 29 | } 30 | 31 | } 32 | else 33 | { 34 | $this->find($user); 35 | } 36 | } 37 | 38 | public function update($fields = array(), $id = null) 39 | { 40 | if (!$id && $this->isLoggedIn()) 41 | { 42 | $id = $this->data()->uid; 43 | } 44 | 45 | if (!$this->_db->update('users', $id, $fields)) 46 | { 47 | throw new Exception('Unable to update the user.'); 48 | } 49 | } 50 | 51 | public function create($fields = array()) 52 | { 53 | if (!$this->_db->insert('users', $fields)) 54 | { 55 | throw new Exception("Unable to create the user."); 56 | } 57 | } 58 | 59 | public function find($user = null) 60 | { 61 | if ($user) 62 | { 63 | $field = (is_numeric($user)) ? 'uid' : 'username'; 64 | 65 | $data = $this->_db->get('users', array($field, '=', $user)); 66 | 67 | if ($data->count()) 68 | { 69 | $this->_data = $data->first(); 70 | return true; 71 | } 72 | } 73 | } 74 | 75 | public function login($username = null, $password = null, $remember = false) 76 | { 77 | if (! $username && ! $password && $this->exists()) 78 | { 79 | Session::put($this->_sessionName, $this->data()->uid); 80 | } 81 | else 82 | { 83 | $user = $this->find($username); 84 | 85 | if ($user) 86 | { 87 | if (Password::check($password, $this->data()->password)) 88 | { 89 | Session::put($this->_sessionName, $this->data()->uid); 90 | 91 | if ($remember) 92 | { 93 | $hash = Hash::unique(); 94 | $hashCheck = $this->_db->get('users_session', array('user_id', '=', $this->data()->uid)); 95 | 96 | if (!$hashCheck->count()) 97 | { 98 | $this->_db->insert('users_session', array( 99 | 'user_id' => $this->data()->uid, 100 | 'hash' => $hash 101 | )); 102 | } 103 | else 104 | { 105 | $hash = $hashCheck->first()->hash; 106 | } 107 | 108 | Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry')); 109 | } 110 | 111 | return true; 112 | } 113 | } 114 | } 115 | 116 | return false; 117 | } 118 | 119 | public function hasPermission($key) 120 | { 121 | $group = $this->_db->get('groups', array('gid', '=', $this->data()->groups)); 122 | 123 | if ($group->count()) 124 | { 125 | $permissions = json_decode($group->first()->permissions, true); 126 | 127 | if ($permissions[$key] == true) 128 | { 129 | return true; 130 | } 131 | } 132 | 133 | return false; 134 | } 135 | 136 | public function exists() 137 | { 138 | return (!empty($this->_data)) ? true : false; 139 | } 140 | 141 | public function logout() 142 | { 143 | $this->_db->delete('users_session', array('user_id', '=', $this->data()->uid)); 144 | 145 | Session::delete($this->_sessionName); 146 | Cookie::delete($this->_cookieName); 147 | } 148 | 149 | public function data() 150 | { 151 | return $this->_data; 152 | } 153 | 154 | public function isLoggedIn() 155 | { 156 | return $this->_isLoggedIn; 157 | } 158 | 159 | public function deleteMe() 160 | { 161 | if ($this->isLoggedIn()) 162 | { 163 | $id = $this->data()->uid; 164 | } 165 | 166 | if (!$this->_db->delete('users', array('uid', '=', $id))) 167 | { 168 | throw new Exception('Unable to update the user.'); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /app/backend/classes/Validation.php: -------------------------------------------------------------------------------- 1 | _db = Database::getInstance(); 13 | $this->_user = new User; 14 | } 15 | 16 | public function check($source, $items = array()) 17 | { 18 | foreach ($items as $item => $rules) 19 | { 20 | foreach ($rules as $rule => $rule_value) 21 | { 22 | 23 | $value = trim($source[$item]); 24 | $item = escape($item); 25 | 26 | if ($rule === 'optional' && ! empty($value)) 27 | { 28 | $this->_optional = true; 29 | } 30 | 31 | if ($rule === 'required' && empty($value)) 32 | { 33 | $this->addError("{$item} is required."); 34 | } 35 | 36 | if ($rule === 'bind' && empty($value) && ! empty($source[$rule_value])) 37 | { 38 | $this->addError("{$item} is required."); 39 | } 40 | 41 | else if (!empty($value)) 42 | { 43 | switch ($rule) 44 | { 45 | case 'min': 46 | 47 | if (strlen($value) < $rule_value) 48 | { 49 | $this->addError("{$item} must be minimum of {$rule_value} character."); 50 | } 51 | break; 52 | 53 | case 'max': 54 | 55 | if (strlen($value) > $rule_value) 56 | { 57 | $this->addError("{$item} must be maximum of {$rule_value} character."); 58 | } 59 | break; 60 | 61 | case 'match': 62 | 63 | if ($value != $source[$rule_value]) 64 | { 65 | $this->addError("{$rule_value} must match {$item}."); 66 | } 67 | break; 68 | 69 | case 'email': 70 | 71 | if (filter_var($value,FILTER_VALIDATE_EMAIL) !== $rule_value) 72 | { 73 | $this->addError("{$item} must valid email format."); 74 | } 75 | break; 76 | 77 | case 'alnum': 78 | 79 | if (ctype_alnum($value) !== $rule_value) 80 | { 81 | $this->addError("{$item} must alphanumeric."); 82 | } 83 | break; 84 | 85 | case 'unique': 86 | $check = $this->_db->get($rule_value, array($item, '=', $value)); 87 | 88 | if ($check->count()) 89 | { 90 | $this->addError("{$item} already exists."); 91 | } 92 | break; 93 | 94 | case 'verify': 95 | $check = $this->_db->get($rule_value, array($item, '=', $value)); 96 | 97 | if (! Password::check($value, $this->_user->data()->password)) 98 | { 99 | $this->addError("Wrong Current Password!."); 100 | } 101 | break; 102 | } 103 | } 104 | } 105 | } 106 | 107 | if (empty($this->_errors)) 108 | { 109 | $this->_passed = true; 110 | } 111 | 112 | return $this; 113 | } 114 | 115 | private function addError($error) 116 | { 117 | $this->_errors[] = $error; 118 | } 119 | 120 | public function errors() 121 | { 122 | return $this->_errors; 123 | } 124 | 125 | public function error() 126 | { 127 | return $this->_errors[0]; 128 | } 129 | 130 | public function passed() 131 | { 132 | return $this->_passed; 133 | } 134 | 135 | public function optional() 136 | { 137 | return $this->_optional; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/backend/core/Config.php: -------------------------------------------------------------------------------- 1 | _pdo = new PDO('mysql:host='.Config::get('mysql/host').';'. 17 | 'dbname='.Config::get('mysql/db_name'), 18 | Config::get('mysql/username'), 19 | Config::get('mysql/password') 20 | ); 21 | } 22 | catch(PDOException $e) 23 | { 24 | die($e->getMessage()); 25 | } 26 | } 27 | 28 | public static function getInstance() 29 | { 30 | if (!isset(self::$_instance)) 31 | { 32 | self::$_instance = new Database(); 33 | } 34 | 35 | return self::$_instance; 36 | } 37 | 38 | public function query($sql, $params = array()) 39 | { 40 | $this->_error = false; 41 | 42 | if ($this->_query = $this->_pdo->prepare($sql)) 43 | { 44 | $x = 1; 45 | 46 | if (count($params)) 47 | { 48 | foreach ($params as $param) 49 | { 50 | $this->_query->bindvalue($x, $param); 51 | $x++; 52 | } 53 | } 54 | 55 | if ($this->_query->execute()) 56 | { 57 | $this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ); 58 | $this->_count = $this->_query->rowCount(); 59 | } 60 | else 61 | { 62 | $this->_error = true; 63 | } 64 | } 65 | 66 | return $this; 67 | } 68 | 69 | public function action($action, $table, $where = array()) 70 | { 71 | if (count($where) === 3) 72 | { 73 | $operators = array('=', '>', '<', '>=', '<='); 74 | 75 | $field = $where[0]; 76 | $operator = $where[1]; 77 | $value = $where[2]; 78 | 79 | if (in_array($operator, $operators)) 80 | { 81 | $sql = "{$action} FROM {$table} WHERE {$field} {$operator} ?"; 82 | 83 | if (!$this->query($sql, array($value))->error()) 84 | { 85 | return $this; 86 | } 87 | } 88 | } 89 | 90 | return false; 91 | } 92 | 93 | public function get($table, $where) 94 | { 95 | return $this->action('SELECT *', $table, $where); 96 | } 97 | 98 | public function delete($table, $where) 99 | { 100 | return $this->action('DELETE', $table, $where); 101 | } 102 | 103 | public function insert($table, $fields = array()) 104 | { 105 | if (count($fields)) 106 | { 107 | $keys = array_keys($fields); 108 | $values = ''; 109 | $x = 1; 110 | 111 | foreach ($fields as $field) 112 | { 113 | $values .= '?'; 114 | 115 | if ($x < count($fields)) 116 | { 117 | $values .= ', '; 118 | } 119 | 120 | $x++; 121 | } 122 | 123 | $sql = "INSERT INTO {$table} (`".implode('`, `', $keys)."`) VALUES ({$values})"; 124 | 125 | if (!$this->query($sql, $fields)->error()) 126 | { 127 | return true; 128 | } 129 | } 130 | 131 | return false; 132 | } 133 | 134 | public function update($table, $id, $fields) 135 | { 136 | $set = ''; 137 | $x = 1; 138 | 139 | foreach ($fields as $name => $value) 140 | { 141 | $set .= "{$name} = ?"; 142 | 143 | if ($x < count($fields)) 144 | { 145 | $set .= ', '; 146 | } 147 | 148 | $x++; 149 | } 150 | 151 | $sql = "UPDATE {$table} SET {$set} WHERE uid = {$id}"; 152 | 153 | if (!$this->query($sql, $fields)->error()) 154 | { 155 | return true; 156 | } 157 | 158 | return false; 159 | } 160 | 161 | public function results() 162 | { 163 | return $this->_results; 164 | } 165 | 166 | public function first() 167 | { 168 | return $this->results()[0]; 169 | } 170 | 171 | public function error() 172 | { 173 | return $this->_error; 174 | } 175 | 176 | public function count() 177 | { 178 | return $this->_count; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /app/backend/core/Hash.php: -------------------------------------------------------------------------------- 1 | Config::get('password/cost'), 10 | 'salt' => Hash::salt() 11 | )); 12 | } 13 | 14 | public static function rehash($hash) 15 | { 16 | return password_rehash($hash, Config::get('password/algo_name'), Config::get('password/cost')); 17 | } 18 | 19 | public static function check($password, $hash) 20 | { 21 | return password_verify($password, $hash); 22 | } 23 | 24 | public static function getInfo($hash) 25 | { 26 | return password_get_info($hash); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/backend/core/Redirect.php: -------------------------------------------------------------------------------- 1 | .panel-heading { 11 | color: #00415d; 12 | background-color: #fff; 13 | border-color: #fff; 14 | text-align:center; 15 | } 16 | .panel-login>.panel-heading a{ 17 | text-decoration: none; 18 | color: #666; 19 | font-weight: bold; 20 | font-size: 15px; 21 | -webkit-transition: all 0.1s linear; 22 | -moz-transition: all 0.1s linear; 23 | transition: all 0.1s linear; 24 | } 25 | .panel-login>.panel-heading a.active{ 26 | color: #029f5b; 27 | font-size: 18px; 28 | } 29 | .panel-login>.panel-heading hr{ 30 | margin-top: 10px; 31 | margin-bottom: 0px; 32 | clear: both; 33 | border: 0; 34 | height: 1px; 35 | background-image: -webkit-linear-gradient(left,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0.15),rgba(0, 0, 0, 0)); 36 | background-image: -moz-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,0.15),rgba(0,0,0,0)); 37 | background-image: -ms-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,0.15),rgba(0,0,0,0)); 38 | background-image: -o-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,0.15),rgba(0,0,0,0)); 39 | } 40 | .panel-login input[type="text"],.panel-login input[type="email"],.panel-login input[type="password"] { 41 | height: 45px; 42 | border: 1px solid #ddd; 43 | font-size: 16px; 44 | -webkit-transition: all 0.1s linear; 45 | -moz-transition: all 0.1s linear; 46 | transition: all 0.1s linear; 47 | } 48 | .panel-login input:hover, 49 | .panel-login input:focus { 50 | outline:none; 51 | -webkit-box-shadow: none; 52 | -moz-box-shadow: none; 53 | box-shadow: none; 54 | border-color: #ccc; 55 | } 56 | .btn-login { 57 | background-color: #59B2E0; 58 | outline: none; 59 | color: #fff; 60 | font-size: 14px; 61 | height: auto; 62 | font-weight: normal; 63 | padding: 14px 0; 64 | text-transform: uppercase; 65 | border-color: #59B2E6; 66 | } 67 | .btn-login:hover, 68 | .btn-login:focus { 69 | color: #fff; 70 | background-color: #53A3CD; 71 | border-color: #53A3CD; 72 | } 73 | .forgot-password { 74 | text-decoration: underline; 75 | color: #888; 76 | } 77 | .forgot-password:hover, 78 | .forgot-password:focus { 79 | text-decoration: underline; 80 | color: #666; 81 | } 82 | 83 | .btn-register { 84 | background-color: #1CB94E; 85 | outline: none; 86 | color: #fff; 87 | font-size: 14px; 88 | height: auto; 89 | font-weight: normal; 90 | padding: 14px 0; 91 | text-transform: uppercase; 92 | border-color: #1CB94A; 93 | } 94 | .btn-register:hover, 95 | .btn-register:focus { 96 | color: #fff; 97 | background-color: #1CA347; 98 | border-color: #1CA347; 99 | } 100 | -------------------------------------------------------------------------------- /app/frontend/assets/css/profile.css: -------------------------------------------------------------------------------- 1 | .user-row { 2 | margin-bottom: 14px; 3 | } 4 | 5 | .user-row:last-child { 6 | margin-bottom: 0; 7 | } 8 | 9 | .dropdown-user { 10 | margin: 13px 0; 11 | padding: 5px; 12 | height: 100%; 13 | } 14 | 15 | .dropdown-user:hover { 16 | cursor: pointer; 17 | } 18 | 19 | .table-user-information > tbody > tr { 20 | border-top: 1px solid rgb(221, 221, 221); 21 | } 22 | 23 | .table-user-information > tbody > tr:first-child { 24 | border-top: 0; 25 | } 26 | 27 | 28 | .table-user-information > tbody > tr > td { 29 | border-top: 0; 30 | } 31 | .toppad 32 | {margin-top:20px; 33 | } 34 | -------------------------------------------------------------------------------- /app/frontend/assets/js/login-form.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | $('#login-form-link').click(function(e) { 4 | $("#login-form").delay(100).fadeIn(100); 5 | $("#register-form").fadeOut(100); 6 | $('#register-form-link').removeClass('active'); 7 | $(this).addClass('active'); 8 | e.preventDefault(); 9 | }); 10 | $('#register-form-link').click(function(e) { 11 | $("#register-form").delay(100).fadeIn(100); 12 | $("#login-form").fadeOut(100); 13 | $('#login-form-link').removeClass('active'); 14 | $(this).addClass('active'); 15 | e.preventDefault(); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /app/frontend/assets/js/profile.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | var panels = $('.user-infos'); 3 | var panelsButton = $('.dropdown-user'); 4 | panels.hide(); 5 | 6 | //Click dropdown 7 | panelsButton.click(function() { 8 | //get data-for attribute 9 | var dataFor = $(this).attr('data-for'); 10 | var idFor = $(dataFor); 11 | 12 | //current button 13 | var currentButton = $(this); 14 | idFor.slideToggle(400, function() { 15 | //Completed slidetoggle 16 | if(idFor.is(':visible')) 17 | { 18 | currentButton.html(''); 19 | } 20 | else 21 | { 22 | currentButton.html(''); 23 | } 24 | }) 25 | }); 26 | 27 | 28 | $('[data-toggle="tooltip"]').tooltip(); 29 | 30 | $('button').click(function(e) { 31 | e.preventDefault(); 32 | alert("This is a demo.\n :-)"); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /app/frontend/includes/errors/404.php: -------------------------------------------------------------------------------- 1 | Oops, That Page Cannot be Found! 2 | -------------------------------------------------------------------------------- /app/frontend/includes/footer.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Footer

4 |
5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/frontend/includes/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?php appName(); ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 |
31 |

My First

32 |

Resize this responsive page to see the effect!

33 | isLoggedIn()): ?> 34 |

Hello, data()->name;?>

35 | 36 |
37 | 38 | -------------------------------------------------------------------------------- /app/frontend/includes/messages.php: -------------------------------------------------------------------------------- 1 | ' . Session::flash('register-success') . ' Login Here'; 6 | } 7 | 8 | if(Session::exists('update-success')) 9 | { 10 | echo '
' . Session::flash('update-success') . '
'; 11 | } 12 | -------------------------------------------------------------------------------- /app/frontend/includes/navbar.php: -------------------------------------------------------------------------------- 1 | 2 | 33 | -------------------------------------------------------------------------------- /app/frontend/pages/home.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

About Me

6 |
Photo of me:
7 |
Fake Image
8 |

Some text about me in culpa qui officia deserunt mollit anim..

9 |

Some Links

10 |

Lorem ipsum dolor sit ame.

11 | 25 |
26 |
27 |
28 |

TITLE HEADING

29 |
Title description, Dec 7, 2017
30 |
Fake Image
31 |

Some text..

32 |

Sunt in culpa qui officia deserunt mollit anim id est laborum consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.

33 |
34 |

TITLE HEADING

35 |
Title description, Sep 2, 2017
36 |
Fake Image
37 |

Some text..

38 |

Sunt in culpa qui officia deserunt mollit anim id est laborum consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.

39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /app/frontend/pages/login.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Login Form

4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 17 |
18 | 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /app/frontend/pages/profile.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |

name); ?>

8 |
9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
Name :name); ?>
Username :username); ?>
Date Joined :joined); ?>
28 | Update Information 29 | Back 30 | Delete Account 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /app/frontend/pages/register.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Register Form

4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /app/frontend/pages/update-account.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Update Information

4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 | 26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /app/setup/php_boilerplate.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.7.4 3 | -- https://www.phpmyadmin.net/ 4 | -- 5 | -- Host: 127.0.0.1 6 | -- Generation Time: Aug 02, 2018 at 12:52 PM 7 | -- Server version: 10.1.28-MariaDB 8 | -- PHP Version: 7.1.10 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET AUTOCOMMIT = 0; 12 | START TRANSACTION; 13 | SET time_zone = "+00:00"; 14 | 15 | 16 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 17 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 18 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 19 | /*!40101 SET NAMES utf8mb4 */; 20 | 21 | -- 22 | -- Database: `php_boilerplate` 23 | -- 24 | 25 | -- -------------------------------------------------------- 26 | 27 | -- 28 | -- Table structure for table `groups` 29 | -- 30 | 31 | CREATE TABLE `groups` ( 32 | `gid` int(11) NOT NULL, 33 | `name` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, 34 | `permissions` text COLLATE utf8mb4_unicode_ci NOT NULL 35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 36 | 37 | -- 38 | -- Dumping data for table `groups` 39 | -- 40 | 41 | INSERT INTO `groups` (`gid`, `name`, `permissions`) VALUES 42 | (1, 'Standard User', ''), 43 | (2, 'Administrator', '{\"admin\": 1,\r\n\"moderator\" :1}'); 44 | 45 | -- -------------------------------------------------------- 46 | 47 | -- 48 | -- Table structure for table `users` 49 | -- 50 | 51 | CREATE TABLE `users` ( 52 | `uid` int(11) NOT NULL, 53 | `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, 54 | `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, 55 | `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, 56 | `joined` datetime NOT NULL, 57 | `groups` int(11) NOT NULL 58 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 59 | 60 | -- 61 | -- Table structure for table `users_session` 62 | -- 63 | 64 | CREATE TABLE `users_session` ( 65 | `id` int(11) NOT NULL, 66 | `user_id` int(11) NOT NULL, 67 | `hash` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL 68 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 69 | 70 | -- 71 | -- Indexes for dumped tables 72 | -- 73 | 74 | -- 75 | -- Indexes for table `groups` 76 | -- 77 | ALTER TABLE `groups` 78 | ADD PRIMARY KEY (`gid`); 79 | 80 | -- 81 | -- Indexes for table `users` 82 | -- 83 | ALTER TABLE `users` 84 | ADD PRIMARY KEY (`uid`); 85 | 86 | -- 87 | -- Indexes for table `users_session` 88 | -- 89 | ALTER TABLE `users_session` 90 | ADD PRIMARY KEY (`id`); 91 | 92 | -- 93 | -- AUTO_INCREMENT for dumped tables 94 | -- 95 | 96 | -- 97 | -- AUTO_INCREMENT for table `groups` 98 | -- 99 | ALTER TABLE `groups` 100 | MODIFY `gid` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; 101 | 102 | -- 103 | -- AUTO_INCREMENT for table `users` 104 | -- 105 | ALTER TABLE `users` 106 | MODIFY `uid` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=20; 107 | 108 | -- 109 | -- AUTO_INCREMENT for table `users_session` 110 | -- 111 | ALTER TABLE `users_session` 112 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; 113 | COMMIT; 114 | 115 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 116 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 117 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 118 | -------------------------------------------------------------------------------- /delete-account.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /logout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /profile.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /register.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /start.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------