├── .gitignore ├── composer.json ├── composer.lock ├── controllers ├── AuthController.php └── SiteController.php ├── core ├── Application.php ├── Controller.php ├── Model.php ├── Request.php ├── Response.php └── Router.php ├── models └── RegisterModel.php ├── public └── index.php └── views ├── _404.php ├── contact.php ├── index.php ├── layouts ├── auth.php └── main.php ├── login.php └── register.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .env 3 | vendor 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lukabrazi/php-mvc-framework", 3 | "autoload": { 4 | "psr-4": { 5 | "app\\": "./" 6 | } 7 | }, 8 | "authors": [ 9 | { 10 | "name": "Lukabrazi111" 11 | } 12 | ], 13 | "require": {} 14 | } 15 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "4977a330a24519aeea2b8f4da0ecb395", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": [], 16 | "platform-dev": [], 17 | "plugin-api-version": "2.3.0" 18 | } 19 | -------------------------------------------------------------------------------- /controllers/AuthController.php: -------------------------------------------------------------------------------- 1 | setLayout('auth'); 14 | return $this->render('login'); 15 | } 16 | 17 | public function register(Request $request) 18 | { 19 | $this->setLayout('auth'); 20 | 21 | $registerModel = new RegisterModel(); 22 | 23 | if ($request->isPost()) { 24 | $registerModel->loadData($request->getBody()); 25 | 26 | if ($registerModel->validate() && $registerModel->register()) { 27 | return 'Success'; 28 | } 29 | } 30 | 31 | return $this->render('register', ['model' => $registerModel]); 32 | } 33 | } -------------------------------------------------------------------------------- /controllers/SiteController.php: -------------------------------------------------------------------------------- 1 | render('index', ['name' => 'Luka']); 12 | } 13 | 14 | public function contact() 15 | { 16 | return $this->render('contact'); 17 | } 18 | } -------------------------------------------------------------------------------- /core/Application.php: -------------------------------------------------------------------------------- 1 | controller = new Controller(); 19 | $this->request = new Request(); 20 | $this->response = new Response(); 21 | $this->router = new Router($this->request, $this->response); 22 | } 23 | 24 | public function run() 25 | { 26 | echo $this->router->resolve(); 27 | } 28 | } -------------------------------------------------------------------------------- /core/Controller.php: -------------------------------------------------------------------------------- 1 | router->renderView($view, $params); 19 | } 20 | 21 | /** 22 | * Set layout name file. 23 | * 24 | * @param $layout 25 | * @return mixed 26 | */ 27 | public function setLayout($layout) 28 | { 29 | return $this->layout = $layout; 30 | } 31 | } -------------------------------------------------------------------------------- /core/Model.php: -------------------------------------------------------------------------------- 1 | $value) { 23 | if (property_exists($this, $key)) { 24 | return $this->{$key} = $value; 25 | } 26 | } 27 | 28 | return true; 29 | } 30 | 31 | public function validate() 32 | { 33 | foreach ($this->rules() as $attribute => $rules) { 34 | $value = $this->{$attribute}; 35 | 36 | foreach ($rules as $rule) { 37 | $ruleName = $rule; 38 | 39 | if (!is_string($rule)) { 40 | $ruleName = $rule[0]; 41 | } 42 | 43 | if ($ruleName === self::$RULE_REQUIRED && !$value) { 44 | $this->addError($attribute, self::$RULE_REQUIRED); 45 | } 46 | 47 | if ($ruleName === self::$RULE_MIN && strlen($value) < $rule['min']) { 48 | $this->addError($attribute, self::$RULE_MIN, $rule); 49 | } 50 | 51 | if ($ruleName === self::$RULE_MAX && strlen($value) < $rule['max']) { 52 | $this->addError($attribute, self::$RULE_MAX, $rule); 53 | } 54 | 55 | if ($ruleName === self::$RULE_EMAIL && !filter_var($value, FILTER_VALIDATE_EMAIL)) { 56 | $this->addError($attribute, self::$RULE_EMAIL, $rule); 57 | } 58 | 59 | if($ruleName === self::$RULE_MATCH && $value !== $this->{$rule['match']}) { 60 | $this->addError($attribute, self::$RULE_MATCH, $rule); 61 | } 62 | } 63 | } 64 | 65 | return empty($this->errors); 66 | } 67 | 68 | abstract public function rules(); 69 | 70 | public function addError($attribute, $rule, $params = []) 71 | { 72 | $message = $this->errorMessages()[$rule] ?? ''; 73 | 74 | foreach ($params as $key => $value) { 75 | $message = str_replace("{{$key}}", $value, $message); 76 | } 77 | 78 | return $this->errors[$attribute][] = $message; 79 | } 80 | 81 | public function errorMessages(): array 82 | { 83 | return [ 84 | self::$RULE_REQUIRED => 'This field is required.', 85 | self::$RULE_EMAIL => 'This field must be valid email address.', 86 | self::$RULE_MAX => 'Max length of this field must be {max}', 87 | self::$RULE_MIN => 'Min length of this field must be {min}', 88 | self::$RULE_MATCH => 'This field must be the same as {match}', 89 | ]; 90 | } 91 | } -------------------------------------------------------------------------------- /core/Request.php: -------------------------------------------------------------------------------- 1 | isGet()) { 29 | foreach ($_GET as $key => $value) { 30 | $body[$key] = filter_input(INPUT_GET, $key, FILTER_SANITIZE_SPECIAL_CHARS); 31 | } 32 | } 33 | 34 | if ($this->isPost()) { 35 | foreach ($_POST as $key => $value) { 36 | $body[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS); 37 | } 38 | } 39 | 40 | return $body; 41 | } 42 | 43 | /** 44 | * Check if method is get from super global variable. 45 | * 46 | * @return bool 47 | */ 48 | public function isGet() 49 | { 50 | return $this->method() === 'get'; 51 | } 52 | 53 | /** 54 | * Get method from request. 55 | * 56 | * @return string 57 | */ 58 | public function method() 59 | { 60 | return strtolower($_SERVER['REQUEST_METHOD']); 61 | } 62 | 63 | /** 64 | * Check if method is post from super global variable. 65 | * 66 | * @return bool 67 | */ 68 | public function isPost() 69 | { 70 | return $this->method() === 'post'; 71 | } 72 | } -------------------------------------------------------------------------------- /core/Response.php: -------------------------------------------------------------------------------- 1 | request = $request; 15 | $this->response = $response; 16 | } 17 | 18 | public function get($path, $callback) 19 | { 20 | return $this->routes['get'][$path] = $callback; 21 | } 22 | 23 | public function post($path, $callback) 24 | { 25 | return $this->routes['post'][$path] = $callback; 26 | } 27 | 28 | /** 29 | * Generate specific route request. 30 | */ 31 | public function resolve() 32 | { 33 | $path = $this->request->getPath(); 34 | $method = $this->request->method(); 35 | $callback = $this->routes[$method][$path] ?? false; 36 | 37 | if (!$callback) { 38 | $this->response->statusCode(404); 39 | return $this->renderView('_404'); 40 | } 41 | 42 | if (is_string($callback)) { 43 | return $this->renderView($callback); 44 | } 45 | 46 | return call_user_func($this->isArray($callback) ?? $callback, $this->request); 47 | } 48 | 49 | public function renderView($view, $params = []) 50 | { 51 | $layoutContent = $this->layoutContent(); 52 | $viewContent = $this->renderOnlyView($view, $params); 53 | return str_replace('{{content}}', $viewContent, $layoutContent); 54 | } 55 | 56 | public function layoutContent() 57 | { 58 | $layout = Application::$app->controller->layout; 59 | ob_start(); 60 | include_once Application::$ROOT_PATH . "/views/layouts/$layout.php"; 61 | return ob_get_clean(); 62 | } 63 | 64 | public function renderOnlyView($view, $params = []) 65 | { 66 | foreach ($params as $key => $value) { 67 | $$key = $value; 68 | } 69 | 70 | ob_start(); 71 | include_once Application::$ROOT_PATH . "/views/$view.php"; 72 | return ob_get_clean(); 73 | } 74 | 75 | public function isArray($callback) 76 | { 77 | if (is_array($callback)) { 78 | Application::$app->controller = new $callback[0]; 79 | $callback[0] = Application::$app->controller; 80 | } 81 | 82 | return $callback; 83 | } 84 | } -------------------------------------------------------------------------------- /models/RegisterModel.php: -------------------------------------------------------------------------------- 1 | [self::$RULE_REQUIRED], 20 | 'lastname' => [self::$RULE_REQUIRED], 21 | 'username' => [self::$RULE_REQUIRED, [self::$RULE_MIN, 'min' => 7]], 22 | 'email' => [self::$RULE_REQUIRED, self::$RULE_EMAIL], 23 | 'password' => [self::$RULE_REQUIRED, [self::$RULE_MAX, 'max' => 60]], 24 | 'confirmPassword' => [self::$RULE_REQUIRED, [self::$RULE_MATCH, 'match' => 'password']], 25 | ]; 26 | } 27 | 28 | public function register() 29 | { 30 | return 'Creating new user'; 31 | } 32 | } -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | router->get('/', [SiteController::class, 'index']); 12 | $app->router->get('/contact', [SiteController::class, 'contact']); 13 | 14 | $app->router->get('/login', [AuthController::class, 'login']); 15 | 16 | $app->router->get('/register', [AuthController::class, 'register']); 17 | $app->router->post('/register', [AuthController::class, 'register']); 18 | 19 | $app->run(); -------------------------------------------------------------------------------- /views/_404.php: -------------------------------------------------------------------------------- 1 |