Introduction
Over the years, I’ve played around with various PHP MVC frameworks, starting off with ZendFrameworks, which is now run under the Laminas project. I’ve dabbled with Symfony before settling on Laravel over the last five-plus years. With each of these frameworks, I’ve always felt that you don’t understand or know what’s going on under the hood with the third-party components, and you’re probably installing many composer components you don’t ever use. The other issue with MVC frameworks is that many changes get introduced, which either means the developer is forever on a learning curve or sticks with older versions of the frameworks, which presents some form of technical debt.
In this series of blogs, I will take a stab at creating a template for my own PHP framework, only relying on Composer to autoload my classes. The only caveat to this is that I’ll use PHPUnit and PHPStan for testing purposes.
Why Build a Custom MVC Framework with Raw PHP?
While many powerful PHP frameworks are available, building my own from scratch using raw PHP allows me complete control over my application. It’s an excellent opportunity to learn how the core components of an MVC framework interact and to implement only the features I genuinely need. This approach ensures your framework remains lightweight, fast, and ideally suited to my project requirements.
Setting Up the Project with Composer
Even though I’m focusing on using raw PHP, Composer still plays a crucial role in our project, specifically for managing testing tools like PHPUnit and PHPStan. Here’s how to get started:
- Install Composer: First I ensured Composer was installed on my system. It can be downloaded from getcomposer.org and follow the instructions.
composer -v - Create a New Project Directory: I created a new directory for my project.
mkdir my-project && cd my-project - Initialize Composer: I then ran the following command to create a
composer.jsonfile, which will manage your dependencies.composer init - Autoloading: Set up Composer’s autoloader to handle my classes. While I’ll be writing everything myself, Composer’s autoloader is still useful to automatically load classes as they’re needed.
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
Setting Up Version Control with GitHub
Version control is essential for managing code changes and collaborating with others. Here’s how I set up Git and connected my project to GitHub:
- Initialize Git: Inside my project directory, I initialized a Git repository.
git init - Create a
.gitignoreFile: Exclude unnecessary files and directories from Git, such asvendor/(where Composer stores dependencies) andnode_modules/./vendor /.env /node_modules - Commit Your Changes: Added all files to the staging area and committed my initial project setup.
git add .
git commit -m "Initial commit with Composer setup" - Create a GitHub Repository: I went to GitHub and created a new repository. These are the command I used to link it to my local project.
git remote add origin https://github.com/yourusername/custom-mvc-framework.git
git branch -M main
git push -u origin main
Project Structure
With Composer and GitHub set up, I started organizing my project with a well-structured directory layout. I decided on the following directories and files as the initial structure:
/src
/Config # Configuration settings and files
/Controllers # Handles incoming requests and returns responses
/Core # Core application logic and utilities
/Definitions # Contains database schema definitions
/Interfaces # Defines interfaces for dependency injection
/Middleware # Manages request preprocessing
/Migrations # Database migration files
/Models # Manages data and business logic
/Services # Contains reusable business logic components
/Views # Renders the user interface
/tests # Contains test cases and test-related files
/public
index.php # Entry point for the application
bootstrap.php # Bootstrapping the application, initializing settings
README.md # Project documentation
phpunit.xml # PHPUnit configuration file
.env # Environment variables (do not commit to Git)
This structure adheres to the MVC pattern, ensuring your application remains organized and scalable as it grows. The public/ directory contains my index.php, while bootstrap.php, README.md, and .env are at the root level.
Explanation of phpunit.xml
The phpunit.xml file is configured to bootstrap your tests using Composer’s autoloader and define a test suite for your application:
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="Application Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
Bootstrap: Points to vendor/autoload.php, which autoloads the necessary classes and files for your tests.
Testsuite: Defines a test suite named “Application Test Suite.”
Directory: Specifies the tests directory as the location for your test files.
Managing the .env File
The .env file stores environment-specific variables, such as database credentials, API keys, and other sensitive data. This file should never be included in your Git repository to prevent sensitive information from being exposed. Add .env to your .gitignore file to ensure it is not tracked by Git.
Protecting .env on the Server
Nginx: I alway use the following configuration in my virtual hosts file on to deny access to .env files:
location ~ /\.env {
deny all;
}
Apache: I add this to my Apache configuration in either the .htaccess file or the virtual hosts configuration:
<FilesMatch "^\.env$">
Require all denied
</FilesMatch>
This prevents unauthorized access to your .env file on the server, adding an extra layer of security.
What’s Next?
In the next part of this series, I’ll walk through several crucial components that will further shape my custom PHP MVC framework:
- Bootstrap.php: Understanding its role in initializing the application.
- Loading .env Variables: How to securely manage and load environment-specific configurations.
- Container for Dependency Injection: Setting up a container to manage dependencies throughout the application.
- Database Connection: Establishing and managing a database connection within the framework.
- Models: Structuring your models to interact with the database efficiently.
- Encryption: Implementing encryption for sensitive data within the application.
I’d love to hear your thoughts on how you would improve the project’s file structure. How would you optimize it, or what would you change? Share your ideas in the comments below.
Stay tuned as I continue this journey of building a custom PHP MVC framework from the ground up. By the end of this series, I’ll have a fully functioning framework that I can use as the foundation for any future projects that I might have.
Feel free to follow along with the code on GitHub. Whether you’re a seasoned developer or just starting out, this project offers valuable insights into the world of PHP development. If you found this post helpful, please share it with others and follow for future updates!