This is a small, simple yet powerful PHP MVC Framework built with vanilla PHP using no frameworks. My goal is to make a self-contained MVC that doesn't depend on any 3rd parties. No installation/composer required. I'm trying my best to lower the learning curve when it comes to MVC.
- First, get the code by downloading the zip or cloning the repo.
- Run composer... oh right, no composer installation is required!
- That's it. To configure, open Swidly/Core/config.php and fill out your server information.
- Create routes, controllers, views, and models.
Also supports templating and multi-language. You can edit the file at: Swidly/lang/en.json. Wrap the word/phrase with {} and the templating system will handle the rest.
You can start the app by using PHP's built-in server by typing:
php -S localhost:8000
Then navigate to localhost:8000 in your browser.
To run the sample, first create a database called blog and add your database information to your Swidly/Core/config.php file. Use the dump.sql to generate the test database.
Note: The root
index.phpfile is only needed when running from the built-in PHP server.
ROOT/
├── bin/
│ └── console
├── Swidly/
│ ├── Core/
│ ├── lang/
│ ├── Middleware/
│ ├── Migrations/ (todo)
│ └── themes/
│ └── default/ (is where you create your MVC)
│ └── theme.php (theme meta)
├── public/
│ └── index.php
├── .htaccess
└── bootstrap.php (must be in root directory)
The configuration settings are stored under Swidly/Core/config.php. You can access the settings in your code by: Swidly::getConfig('db::host'). You can also set a default value by: Swidly::getConfig('db::host', 'localhost').
This static method returns the value of a configuration parameter with the given name. If the parameter is not found or is empty, it returns the default value.
Parameters:
$name(string): The name of the configuration parameter to retrieve.$default(mixed): The default value to return if the parameter is not found or is empty. Default is an empty string.
Return Value:
- (mixed): The value of the configuration parameter, or the default value if the parameter is not found or is empty.
This method sets the value of a configuration parameter with the given name.
Parameters:
$name(string): The name of the configuration parameter to set.$value(mixed): The value to set for the configuration parameter.
Return Value:
- (void): This method does not return anything.
Throws:
SwidlyException: If the configuration file cannot be parsed.
This method sets the values of multiple configuration parameters at once.
Parameters:
$config(array): An associative array of configuration parameter names and values.
Return Value:
- (void): This method does not return anything.
The Router translates URLs into controllers and actions. Routes are added to the front controller. A sample home route is included that routes to the index action in the Home controller.
Routes are located in the Swidly/routes.php file.
To add a route, you can use the $this->get('{path}', '{controller}::{action}') method:
// Give a name to your route so you can access it later in your controller
$this->get('', 'HomeController::Index')->name('home');
$this->post('posts/blog', 'BlogController::AddPost');You can also create a route without a controller:
$this->get('/post/:id', function($request, $response) {
$id = $request->get('id');
echo 'The post id is: ' . $id;
});
// or an optional parameter
$this->get('/post/?:id', function($request, $response) {
$id = $request->get('id', '0');
echo 'The post id is: ' . $id;
});Add middleware with your request. Middleware is an action that can occur before the request is sent to the controller. You can validate a request, change a parameter in a request, or make sure a user is logged in before continuing a request.
$this->get('/post/:id', function($request, $response) {
$id = $request->get('id');
// Now if the Id is greater than 5, it will be set to 5
echo 'The post id is: ' . $id;
})->registerMiddleware(function($request, $response) {
$id = (int) $request->get('id');
if($id > 5) {
$request->set('id', '5');
}
});Validate if a user is logged in:
$this->get('/post/:id', function($request, $response) {
$id = $request->get('id');
echo 'The post id is: ' . $id . '. Welcome ' . $request->get('user_username');
})->registerMiddleware(function($request, $response) {
if(!$request->is_authenticated) {
$response->setContent('You must be logged on to see this page');
$response->content();
exit;
} else {
$user = $request->getUser();
$request->set('user_username', $user->username);
}
});Routes can also be created in a controller using attributes. You can also add middleware to your routes.
Syntax: #[Route('{method}', '{path}', '{route name}' (optional))]
class PostController extends Controller {
#[Route(methods: ['GET'], path: ['/blog', '/posts'], name: 'posts')]
#[Middleware(AuthMiddleware::class)]
function Index($req, $res) {
$posts = $this->model->findAll();
$this->render('post', ['posts' => $posts]);
}
}Syntax: #[RouteGroup(prefix: '[name]')]
#[RouteGroup(prefix: 'blog')]
class BlogController extends Controller {
#[Route(methods: ['GET'], path: '/')]
public function Index($req, $res) {
$model = Model::load('BlogModel');
$blogs = $model->findAll();
$this->render('blog', ['blogs' => $blogs]);
}
}All routes within this controller have a prefix /blog.
Middlewares can be stored in the Swidly/Middleware directory.
Controllers respond to user actions (clicking on a link, submitting a form, etc.). Controllers are classes that extend the Swidly\Core\Controller class.
Controllers are stored in the Swidly/Controllers folder. A sample Home controller is included. Controller classes need to be in the Swidly/Controllers namespace. You can add subdirectories to organize your controllers, so when adding a route for these controllers you need to specify the namespace (see the routing section above).
Controller classes contain methods that are the actions. To create an action, add the Action suffix to the method name. The sample controller in Swidly/Controllers/HomeController.php has a sample index action.
You can access route parameters (for example the id parameter shown in the route examples above) in actions via the $request->get('id') property.
To quickly create a controller/route, run this command in the terminal:
php bin/console make:controller contact
This will generate a controller called Contact in the controllers directory within your selected theme (e.g., default/) directory:
<?php
namespace Swidly\themes\default\controllers;
use Swidly\Core\Attributes\Middleware;
use Swidly\Core\Factory\CommandFactory;
use Swidly\Core\Controller;
use Swidly\Core\Attributes\Route;
use Swidly\Core\Swidly;
use Swidly\Core\SwidlyException;
use Swidly\Middleware\CsrfMiddleware;
/**
* @throws SwidlyException
*/
class ContactController extends Controller {
#[Route(methods: ['GET'], path: '/contact')]
public function Index($req, $res) {
echo 'This is Contact controller.';
}
}Views are used to display information (normally HTML). View files go in the Swidly/themes/{themename}/views/ folder. Views can be in one of two formats: standard PHP, but with just enough PHP to show the data. No database access or anything like that should occur in a view file. You can render a standard PHP view in a controller, optionally passing in variables, like this:
#[Route(methods: ['GET'], path: '/about', name: 'about')]
function About($req, $res) {
$this->render('about', [
'data' => [
'first_name' => 'John',
'last_name' => 'Doe',
'dob' => '01/01/2000',
'body' => "this is the body of the about page"
],
]);
}In the view, you can get the passed parameters like so:
<x-inc.header />
<h1>About me</h1>
<p>Hello, my name is <strong>{first_name} {last_name}</strong></p>
<p>I was born on <strong>{dob}</strong></p>
<p>Here is a brief summary about me: <br/> {body}</p>
<x-inc.footer />Note: Whatever is within the curly brackets {} will display the data with that name. Use the curly brackets also to display different languages. For example, {hello} will display hola if the Swidly/Core/lang/es.json has a config for "hello": "hola" and 'default_lang' => 'es' is set in the config.php file.
Use <x-header /> to include other views within another view as a component. This example will render the header component in the about page.
Forms:
@csrf- CSRF token input@method('PUT')- HTTP method spoofing
Authentication:
@auth ... @endauth- Show content only to authenticated users@guest ... @endguest- Show content only to guests
Conditionals:
@if($condition) ... @endif@elseif($condition)@else@isset($var) ... @endisset@empty($var) ... @endempty
Loops:
@foreach($items as $item) ... @endforeach@for($i = 0; $i < 10; $i++) ... @endfor@while($condition) ... @endwhile@continue/@continue(2)- Skip iteration@break/@break(2)- Break loop
Utilities:
@php ... @endphp- Raw PHP blocks@dd($variable)- Dump and die@dump($variable)- Var dump@json($data)- JSON encode with pretty print
Models are used to get and store data in your application. They know nothing about how this data is to be presented in the views. Models extend the Swidly\Core\Model class and use PDO to access the database. They're stored in the Swidly/Models folder. A sample post model class is included in Swidly/Models/PostModel.php.
You must set the $table property of the model. The $idField is also required.
In the model, you have to tell the model what is a column in the table by using the Column attribute:
#[Column()]
private ?string $title = null;The ID column is required:
#[Column()]
public int $id;You can then get data from the database in the controller like so:
$this->model->findAll();
// or
$this->model->findBy(['id' => '123']);This is an example of how to insert new data into the database:
$db = \Swidly\Core\DB::create();
$db->table('post')->insert([
'post_title' => 'post title',
'poster_id' => 123,
'post_body' => 'this is an example post'
]);Insert data using an entity, for example PostModel:
#[Route(methods: ['POST'], path: ['/posts/add'], name: ['addPost'])]
function AddPost($req, $res) {
$post = new PostModel();
$post->setTitle($req->get('title'));
$post->setBody($req->get('content'));
$post->setCreatedAt('2022-11-11 02:46:00');
$post->save();
}#[Route(methods: ['POST'], path: '/posts/update', name: 'updatePost')]
function UpdatePost($req, $res) {
$postModel = $this->model->find(['id' => 2]);
$postModel->setTitle($req->get('title'));
$postModel->save();
}To use the Store class in your PHP project, you will need to include the Store.php file in your PHP script using the require_once statement. Once you have included the file, you can call the static methods of the Store class to start a session, save values to the session, retrieve a CSRF token, and check if a key exists in the session.
<?php
require_once 'path/to/Store.php';
// Start a session
Store::start();
// Save a value to the session
Store::save('username', 'JohnDoe');
// Retrieve the value from the session
$username = Store::get('username');
echo $username; // Output: JohnDoeYou can use the other static methods of the Store class in a similar way to handle sessions in your PHP project.
The File class is a PHP class that provides methods for handling files. It has static methods for reading files, reading JSON files, reading PHP files as arrays or objects, copying files, and converting objects to JSON. The class is part of the Swidly\Core namespace.
readFile($path): Reads the contents of a file and returns them as a string.readJson($path): Reads the contents of a JSON file and returns them as a JSON-encoded string.readArray($path): Reads the contents of a PHP file and returns them as an array or an object.copyFile($source, $destination, $keepOriginal): Copies a file from the source path to the destination path. If$keepOriginalis set totrue, the original file will be kept; otherwise, it will be deleted.toJSON(): Converts an object to a JSON-encoded string.
All of the methods in the File class throw a SwidlyException if there is an error reading or copying the file.
<?php
require_once 'path/to/File.php';
// Read the contents of a file
$content = File::readFile('path/to/file.txt');
echo $content; // Output: The contents of the fileYou can use the other static methods of the File class in a similar way to handle files in your PHP project.
The Hook class is a PHP class that provides a simple way to implement hooks in your PHP project. It allows you to register callbacks that can be executed at specific points in your code. The class is part of the Swidly\Core namespace.
__construct($name, $allowMultipleCalls): Initializes a new instance of theHookclass with the specified name and whether multiple calls are allowed.addAction($callback, $priority, $runOnce): Adds a new action to the hook with the specified callback, priority, and whether the action should only be run once.doCallback(): Executes all the actions registered to the hook.getHook($name): Returns the hook with the specified name.getActions(): Returns all the actions registered to the hook.setDone(): Marks the hook as done.
The Hook class uses the Action class to represent each action registered to the hook. The Action class contains a callback, priority, and whether the action should only be run once.
<?php
require_once 'path/to/Hook.php';
// Create a new hook
$hook = new Hook('my_hook', true);
// Add a new action to the hook
$hook->addAction(function($arg1, $arg2) {
echo "Action 1: $arg1, $arg2\n";
}, Priority::NORMAL, false);
// Add another action to the hook
$hook->addAction(function($arg1, $arg2) {
echo "Action 2: $arg1, $arg2\n";
}, Priority::HIGH, false);
// Execute the hook
$hook->doCallback('Hello', 'World');Output:
Action 2: Hello, World
Action 1: Hello, World
You can use the Hook class to implement hooks in your PHP project to execute callbacks at specific points in your code.
That's it. No download or installation required.