PHPackages                             omarelgabry/miniphp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. omarelgabry/miniphp

ActiveProject[Framework](/categories/framework)

omarelgabry/miniphp
===================

A small, simple PHP MVC framework skeleton that encapsulates a lot of features surrounded with powerful security layers.

v3.0(10y ago)1646552[4 issues](https://github.com/OmarElGabry/miniPHP/issues)[2 PRs](https://github.com/OmarElGabry/miniPHP/pulls)MITPHPPHP &gt;=5.5.0

Since Jul 6Pushed 5y ago21 watchersCompare

[ Source](https://github.com/OmarElGabry/miniPHP)[ Packagist](https://packagist.org/packages/omarelgabry/miniphp)[ Docs](https://github.com/OmarElGabry/miniPHP)[ RSS](/packages/omarelgabry-miniphp/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (2)Versions (4)Used By (0)

[![miniPHP](https://raw.githubusercontent.com/OmarElGabry/miniPHP/master/public/img/backgrounds/background.png)](https://raw.githubusercontent.com/OmarElGabry/miniPHP/master/public/img/backgrounds/background.png)

miniPHP
=======

[](#miniphp)

[![Build Status](https://camo.githubusercontent.com/76ca52fcf58add87312a9c9d73b772bb95e38b28a7cbd2ab462ca3194ee0eceb/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4f6d6172456c47616272792f6d696e695048502f6261646765732f6275696c642e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/OmarElGabry/miniPHP/build-status/master)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/200765fd8d9713a0035fa726a2ba6fe64cc9935a092bfedddc5728792823b352/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4f6d6172456c47616272792f6d696e695048502f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/OmarElGabry/miniPHP/?branch=master)[![Code Climate](https://camo.githubusercontent.com/90c6c0a4bb3a4fffe33f5d2f34dec9bcb7bfc9dc767982dc06a030a6e053e4b6/68747470733a2f2f636f6465636c696d6174652e636f6d2f6769746875622f4f6d6172456c47616272792f6d696e695048502f6261646765732f6770612e737667)](https://codeclimate.com/github/OmarElGabry/miniPHP)[![Dependency Status](https://camo.githubusercontent.com/4832bf3b20df498a37ddae374a83b3ba8680ba9797f22a3df49edba7880f07be/68747470733a2f2f7777772e76657273696f6e6579652e636f6d2f757365722f70726f6a656374732f3535616538356464333836353632303031383030303030312f62616467652e7376673f7374796c653d666c6174)](https://www.versioneye.com/user/projects/55ae85dd3865620018000001)

[![Latest Stable Version](https://camo.githubusercontent.com/1897e68907747fc1a659e4ef8fbb26429730a065000b6410d7d5946c96373129/68747470733a2f2f706f7365722e707567782e6f72672f6f6d6172656c67616272792f6d696e697068702f762f737461626c65)](https://packagist.org/packages/omarelgabry/miniphp)[![License](https://camo.githubusercontent.com/3f7d8ff3221245bcbc6740bc75b62399b125b3d41eb0605d9f16a97d1f50cd2d/68747470733a2f2f706f7365722e707567782e6f72672f6f6d6172656c67616272792f6d696e697068702f6c6963656e7365)](https://packagist.org/packages/omarelgabry/miniphp)

A small, simple PHP MVC framework skeleton that encapsulates a lot of features surrounded with powerful security layers.

miniPHP is a very simple application, useful for small projects, helps to understand the PHP MVC skeleton, know how to authenticate and authorize, encrypt data and apply security concepts, sanitization and validation, make ajax calls and more.

It's not a full framework, nor a very basic one but it's not complicated. You can easily install, understand, and use it in any of your projects.

It's indented to remove the complexity of the frameworks. Things like routing, authentication, authorization, manage user session and cookies, and so on are not something I've invented from the scratch, however, they are aggregation of concepts already implemented in other frameworks, but, built in a much simpler way, So, you can understand it, and take it further.

If you need to build bigger application, and take the advantage of most of the features available in frameworks, you can see [CakePHP](http://cakephp.org/), [Laravel](http://laravel.com/), [Symphony](http://symfony.com/).

Either way, It's important to understand the PHP MVC skeleton, and know how to authenticate and authorize, learn about security issues and how can you defeat against, and how to build you own application using the framework.

Documentation
-------------

[](#documentation)

Full Documentation can be also found [here](http://omarelgabry.github.io/miniPHP/) — created by GitHub automatic page generator.

Index
-----

[](#index)

- [Demo](#live-demo)
- [Installation](#installation)
- [Routing](#routing)
- [Controller](#controller)
- [Components(Middlewares)](#components)
    - [Authentication](#authentication)
        - [Session](#session)
        - [Cookies](#cookies)
    - [Authorization](#authorization)
    - [Security](#security)
        - [HTTP Methods](#http-method)
        - [Domain Validation](#referer)
        - [Form Tampering](#form-tampering)
        - [CSRF](#csrf)
        - [htaccess](#htaccess)
    - [Turn on/off Components](#turn-on-off-components)
- [Views](#views)
- [Models](#models)
- [Login](#login)
    - [User Verification](#user-verification)
    - [Forgotten Password](#forgotten-password)
    - [Brute-Force attack](#brute-force)
    - [Captcha](#captcha)
    - [Block IP Addresses](#block-ip)
- [Database](#database)
- [Encryption](#encryption)
- [Validation](#validation)
- [Errors &amp; Exceptions](#errors-exceptions)
- [Logger](#logger)
- [Email](#email)
- [Configurations](#configurations)
- [JavaScript &amp; Ajax](#js)
- [Application(Demo)](#app-demo)
    - [Intro](#intro-demo)
    - [Installation](#installation-demo)
    - [User Profile](#profile)
    - [Files](#files)
    - [News Feed &amp; Posts &amp; Comments](#newsfeed-posts-comments)
    - [Admin](#admin)
    - [Notifications](#notifications)
    - [Report Bugs](#bugs)
    - [Backups](#backups)
- [ToDo Application(Step By Step Implementation)](#todo)
- [Support](#support)
- [Contribute](#contribute)
- [Dependencies](#dependencies)
- [License](#license)

Demo
------------------------------------------

[](#demo-)

A live demo is available [here](https://miniphp.ga/). The live demo is for the demo application built on top of this framework in this [section](#app-demo). Thanks to [@Everterstraat](https://github.com/Everterstraat).

> Some features mighn't work in the demo.

Installation
-----------------------------------------------------

[](#installation-)

Install via [Composer](https://getcomposer.org/doc/00-intro.md)

```
	composer install

```

Routing
-------------------------------------------

[](#routing-)

Whenever you make a request to the application, it wil be directed to *index.php* inside public folder. So, if you make a request: `http://localhost/miniPHP/User/update/412 `. This will be splitted and translated into

- Controller: User
- Action Method: update
- Arguemtns to action method: 412

In fact, htaccess splits everything comes after `http://localhost/miniPHP ` and adds it to the URL as querystring argument. So, this request will be converted to: `http://localhost/miniPHP?url='User/update/412' `.

Then `App` Class, Inside `splitUrl()`, will split the query string `$_GET['url']` into controller, action method, and any passed arguments to action method.

In `App` Class, Inside `run()`, it will instantiate an object from controller class, and make a call to action method, passing any arguments if exist.

Controller
-------------------------------------------------

[](#controller-)

After the `App` Class intantiates controller object, It will call `$this->controller->startupProcess()` method, which in turn will trigger 3 consecutive events/methods:

1. `initialize()`: Use it to load components
2. `beforeAction()`: Perform any logic actions before calling controller's action method
3. `triggerComponents()`: Trigger startup() method of loaded components

The constructor of `Controller` Class **shouldn't** be overridden, instead you can override the `initialize()` &amp; `beforeAction()` methods in the extending classes.

After the startup process of the constrcutor finishes it's job, Then, the requested action method will be called, and arguments will be passed(if any).

Components(Middlewares)
--------------------------------------------------------------

[](#componentsmiddlewares-)

Components are the middlewares. They provide reusable logic to be used as part of the controller. Authentication, Authorization, Form Tampering, and Validate CSRF Tokens are implemented inside Components.

It's better to pull these pieces of logic out of controller class, and keep all various tasks and validations inside these Components.

Every component inherits from the base/super class called `Component`. Each has a defined task. There are two components, one for called *Auth* for Authentication and Authorization, and the other one called *Security* for other Security Issues.

They are very simple to deal with, and they will be called inside controller constructor.

### Authentication

[](#authentication-)

Is user has right credentials?

#### Session

[](#session)

The AuthComponent takes care of user session.

- Prevent Session Concurrency
    - There can't be 2 users logged in with same user credentials.
- Defeat against Session Hijacking &amp; Fixation
    - HTTP Only with session cookies
    - Whenever it's possible, It's Highly Recommended to use Secured connection(SSL).
    - Regenerate session periodically and after actions like login, forgot password, ...etc.
    - Validate user's IP Address and User agent(initially will be stored in session). Although they can be faked, It's better to keep them as part of validation methods.
- Session Expiration
    - Session will expire after certain duration(&gt;= 1 day)
    - Session cookie in browser is also configured to be expired after (&gt;= 1 week)
- Session accessible only through the HTTP protocol
    - This is important so sessions won't be accessible by JS.

#### Cookies

[](#cookies)

- Remember Me Tokens
    - User can keep himself logged in using cookies
    - HTTP Only with cookies
    - Whenever it's possible, It's Highly Recommended to use Secured connection(SSL).
    - Cookies stored in browser are attached with tokens and Encrypted data
    - Cookies in browser are also configured to be expired after (&gt;= 2 weeks)

### Authorization

[](#authorization-)

Do you have the right to access or to perform X action?. The *Auth* Component takes care of authorization for each controller. Thus, each controller should implement `isAuthorized()` method. What you need to do is to return `boolean` value.

So, for example, in order to check if current user is admin or not, you would do something like this:

```
    // AdminController

    public function isAuthorized(){

        $role = Session::getUserRole();
        if(isset($role) && $role === "admin"){
            return true;
        }
        return false;
    }
```

If you want to take it further and apply some permission rules, There is a powerful class called `Permission` responsible for defining permission rules. This class allows you to define "Who is allowed to perform specific action method on current controller".

So, for example, in order to allow admins to perform any action on notes, while normal users can only edit their notes:

```
   // NotesController

   public function isAuthorized(){

        $action = $this->request->param('action');
        $role 	= Session::getUserRole();
        $resource = "notes";

		// only for admins
		// they are allowed to perform all actions on $resource
        Permission::allow('admin', $resource, ['*']);

		// for normal users, they can edit only if the current user is the owner
		Permission::allow('user', $resource, ['edit'], 'owner');

        $noteId = $this->request->data("note_id");
        $config = [
            "user_id" => Session::getUserId(),
            "table" => "notes",
            "id" => $noteId
        ];

		// providing the current user's role, $resource, action method, and some configuration data
		// Permission class will check based on rules defined above and return boolean value
		return Permission::check($role, $resource, $action, $config);
    }
```

Now, you can check authorization based on user's role, resource, and for each action method.

### Security

[](#security-)

The SecurityComponent takes care of various security tasks and validation.

#### HTTP Method

[](#http-method)

It's important to restrict the request methods. As an example, if you have an action method that accepts form values, So, ONLY POST request will be accepted. The same idea for Ajax, GET, ..etc. You can do this inside `beforeAction() ` method.

```
    // NotesController

    public function beforeAction(){

        parent::beforeAction();

        $actions = ['create', 'delete'];

        $this->Security->requireAjax($actions);
        $this->Security->requirePost($actions);
    }
```

Also if you require all requests to be through secured connection, you can configure the whole controller, or specific actions to redirect all requests to HTTPS instead of HTTP.

```
    // NotesController

    public function beforeAction(){

        parent::beforeAction();

        $actions = ['create', 'delete'];	// specific action methods
        $actions = ['*'];		        	// all action methods

        $this->Security->requireSecure($actions);
    }
```

#### Domain Validation

[](#domain-validation)

It checks &amp; validates if request is coming from the same domain. Although they can be faked, It's good to keep them as part of our security layers.

#### Form Tampering

[](#form-tampering)

Validate submitted form coming from POST request. The pitfall of this method is you need to define the expected form fields, or data that will be sent with POST request.

By default, the framework will validate for form tampering when POST request is made, and it will make sure the CSRF token is passed with the form fields. In this situation, if you didn't pass the CSRF token, it will be considered as a Security thread.

- Unknown fields cannot be added to the form.
- Fields cannot be removed from the form.

```
    // NotesController

    public function beforeAction(){

        parent::beforeAction();

        $action = $this->request->param('action');
        $actions = ['create', 'delete'];

        $this->Security->requireAjax($actions);
        $this->Security->requirePost($actions);

        switch($action){
            case "create":
                $this->Security->config("form", [ 'fields' => ['note_text']]);
                break;
            case "delete":
            	// If you want to disable validation for form tampering
            	// $this->Security->config("validateForm", false);
                $this->Security->config("form", [ 'fields' => ['note_id']]);
                break;
        }
    }
```

#### CSRF Tokens

[](#csrf-tokens)

CSRF Tokens are important to validate the submitted forms, and to make sure they aren't faked. A hacker can trick the user to make a request to a website, or click on a link, and so on.

They are valid for a certain duration(&gt;= 1 day), then it will be regenerated and stored in user's session.

CSRF validation is disabled by default. If you want to validate the CSRF token, then assign `validateCsrfToken` to `true` as shown in the example below. CSRF validation will be forced when request is POST and form tampering is enabled.

Now, You do not need to manually verify the CSRF token on every requests. The *Security* Component will verify token in the request versus the token stored in the session.

```
    // NotesController

    public function beforeAction(){

        parent::beforeAction();

		$action = $this->request->param('action');
		$actions = ['index'];

        $this->Security->requireGet($actions);

        switch($action){
            case "index":
                $this->Security->config("validateCsrfToken", true);
                break;
        }
    }
```

CSRF tokens are generated per session. You can either add a hidden form field, or in the URL as query parameter.

**Form**

`
