{"id":21646192,"url":"https://github.com/jvspeed74/php-web-framework","last_synced_at":"2026-04-30T11:34:36.150Z","repository":{"id":237742120,"uuid":"795156708","full_name":"jvspeed74/PHP-Web-Framework","owner":"jvspeed74","description":"This repository hosts a lightweight PHP MVC (Model-View-Controller) framework designed for building robust and scalable web applications. The framework is templated with a Fitness Application with select features such as inventory management and shopping cart functionality.","archived":false,"fork":false,"pushed_at":"2024-07-05T03:45:56.000Z","size":5221,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-19T22:42:01.712Z","etag":null,"topics":["bootstrap","javascript","mariadb","object-oriented-programming","php","phpmyadmin","web-development","xampp-server"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jvspeed74.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-02T17:35:08.000Z","updated_at":"2025-02-08T10:24:42.000Z","dependencies_parsed_at":"2024-08-23T14:44:11.294Z","dependency_job_id":"91caecd2-752e-49a1-9989-7f27d51b9bbd","html_url":"https://github.com/jvspeed74/PHP-Web-Framework","commit_stats":null,"previous_names":["jvspeed74/mvc-framework","jvspeed74/php-oop-mvc-framework","jvspeed74/php-web-framework"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/jvspeed74/PHP-Web-Framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jvspeed74%2FPHP-Web-Framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jvspeed74%2FPHP-Web-Framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jvspeed74%2FPHP-Web-Framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jvspeed74%2FPHP-Web-Framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jvspeed74","download_url":"https://codeload.github.com/jvspeed74/PHP-Web-Framework/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jvspeed74%2FPHP-Web-Framework/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32463892,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T22:27:22.272Z","status":"online","status_checked_at":"2026-04-30T02:00:05.929Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bootstrap","javascript","mariadb","object-oriented-programming","php","phpmyadmin","web-development","xampp-server"],"created_at":"2024-11-25T06:43:41.515Z","updated_at":"2026-04-30T11:34:31.142Z","avatar_url":"https://github.com/jvspeed74.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MVC Framework Guide\n\n## Table of Contents\n\n1. [Project Overview](#project-overview)\n2. [Installation](#installation)\n3. [Folder Structure](#folder-structure)\n4. [Controllers](#controllers)\n5. [Models](#models)\n6. [Views](#views)\n\n## Project Overview\n\n- **Objective:** Design and develop a data-driven, interactive web application using PHP and MariaDB.\n- **Focus:** Implement the MVC (Model-View-Controller) software design pattern to ensure a well-structured and\n  maintainable codebase.\n- **User Experience:** Prioritize usability and interactivity to create a seamless experience for users of all levels.\n\n### Features\n\n#### 1. User Authentication\n\n- Allow users to register accounts and securely log in.\n- Authentication security measures preventing SQL injection protect user data.\n\n#### 2. E-commerce Store\n\n- The website hosts shopping cart functionality tied to a user account.\n- Users can add, remove, update, and checkout products in their cart.\n\n#### 3. Session Management\n\n- User sessions are tracked, maintain login state, and shopping cart data across multiple visits.\n\n## Installation\n\n### Prerequisites\n\n- **XAMPP**\n    - Free and open-source cross-platform web server solution stack package.\n        - Minimum Version: 8.0\n        - [XAMPP Download Page](https://www.apachefriends.org/download.html)\n\n1. **Clone the repository:**\n    ```\n    git clone https://github.com/jvspeed74/PHP-Web-Framework.git\n    ```\n2. **Copy the repository files to XAMPP's htdocs directory:**\n    - Your directory structure should look like `xampp/htdocs/PHP-Web-Framework`\n\n3. **Start XAMPP:**\n    - Enable `Apache Web Server` and `MySQL Database` from the XAMPP Control Panel.\n\n4. **Import the Database:**\n    - Open phpMyAdmin by heading to `http://localhost/phpmyadmin/` in a web browser (Google, Edge, ect)\n    - From the navigation bar, click on the `Import` tab\n    - In the topmost section, click choose file and import `fitnesss_db.sql` from the repository.\n    - Scroll to the bottom of the page and click `Import`\n\n5. **Run the application:**\n    - Open a web browser and navigate to `http://localhost/PHP-Web-Framework/`\n    - A fitness themed application should be displayed.\n\n## Folder Structure\n\nThe project follows the following folder structure:\n\n```\nproject-root/  \n│  \n├── app/  \n│ ├─── controllers/  \n│ ├─── core/  \n│ ├─── models/  \n│ ├─── exceptions/  \n│ └─── views/  \n│  \n├── public/  \n│ └─── assets/    \n│ └─── css/    \n│ └─── js/    \n│ └─── index.php    \n│  \n├── vendor/  \n│ └─── autoloader.php\n```\n\n## Controllers\n\nControllers handle incoming requests, process data, and return responses. They reside in the `app/controllers/`\ndirectory. The files themselves follow a two word `PascalCase` naming convention. The last word must be `Controller` in\norder for the class to be recognized by the router/dispatcher.\n\nTo declare a controller:\n\n1. Create a new PHP file in the `app/controllers/` directory.\n2. Define a class extending the base controller class.\n3. Add methods corresponding to different routes or actions.\n4. Use soft exception handling for higher-level operations.\n\nExample:\n\n```php\n// app/controllers/ProductController.php\n\nclass ProductController extends Controller {\n\n    // Point the model property to the model representing the object (i.e. ProductModel)\n    public function __construct() {\n        $this-\u003emodel = ProductHandler::getInstance();\n    }\n\n    public function index() {\n        // Controller logic for the index route\n    }\n    \n    public function show($id) {\n        // Controller logic for showing a single product\n    }\n    \n    // Other controller methods...\n}\n```\n\nThe base `Controller` class declares two properties. One for the instance of the model and another for the session\nmanager instance.\n\n```php\n// app/core/Controller.php\n\nabstract class Controller {\n    /**\n     * @var object|null The model instance associated with the controller.\n     */\n    protected ?object $model = null;\n    protected ?SessionManager $session = null;\n    \n}\n```\n\nThe `SessionManager` class is the endpoint for all $_SESSION variable interactions. Let's take a look at the class.\n\n```php\n// app/core/SessionManager.php\n\nclass SessionManager {\n    private static SessionManager $_instance;\n    \n    private function __construct() {\n        // Prevent instantiation\n    }\n    \n    // Get the singleton instance of the SessionManager class.\n    public static function getInstance(): SessionManager {\n        if (!isset(self::$_instance)) {\n            self::$_instance = new self();\n        }\n        return self::$_instance;\n    }\n    \n    // Start a session if not already started.\n    public function startSession(): void {\n        if (session_status() == PHP_SESSION_NONE) {\n            session_start();\n        }\n    }\n    \n    // Set session variables.\n    public function set(array $data): void {\n        foreach ($data as $key =\u003e $value) {\n            $_SESSION[$key] = $value;\n        }\n    }\n    \n    // Get the value of a session variable.\n    public function get(string $key): mixed {\n        return $_SESSION[$key] ?? null;\n    }\n    \n    // Destroy the session.\n    public function destroy(): void {\n        session_destroy();\n    }\n    \n    // Check if a session is active.\n    public function isActive(): bool {\n        return session_status() === PHP_SESSION_ACTIVE;\n    }\n}\n```\n\nThe purpose of the `SessionManager` class is to prevent direct use of session variables. The class provides a clean\ninterface of session interactions. Overall it aims to reduce code duplication and the potential of errors if a\nsession variable doesn't exist.\n\n## Models\n\nModels handle data manipulation and interaction with the database. They reside in the `app/models/` directory.\n\n### Declaration\n\nTo declare a model:\n\n1. Create a new PHP file in the `app/models/` directory.\n2. Define a class extending the base model class.\n3. Implement methods for CRUD operations and data retrieval.\n4. Use hard exception handling for lower-level operations.\n\nExample:\n\n```php\n// app/models/ProductModel.php\n\n// Represents a model for product.\nclass ProductModel extends Model {\n    \n    // Fetches all products from the database.\n    public function fetchAll(): array {\n        // ...\n    } \n    \n    // Fetching the products that match search terms\n    public function fetchBySearch($terms){\n        // ..\n    }\n    \n    // Other class related methods\n}\n```\n\nThe `Database` class is instantiated in the base model class as the `db` property. There is no intervention required in\nthis process. The `getInstance()` method ensures that the controller can communicate with the model without multiple\ncopies of the class being declared. It would be far from optimal to connect to the database more times than necessary.\nSee below:\n\n```php\n// app/core/Model.php\n\nabstract class Model {\n\n    protected Database $db;  // The database connection instance.\n    \n    /**\n     * Model constructor.\n     * Initializes the database connection.\n     */\n    protected function __construct() {\n        $this-\u003edb = Database::getInstance();\n    }\n    \n    // Static instance of the class that inherits this method.\n    abstract static public function getInstance();\n}\n```\n\n### Database\n\nThis framework utilizes MariaDB powered through XAMPP. The SQL syntax is fairly similar to MySQL with a few minor\ndeviations. All direct database operations are handled through the `Database` class. Models should obtain an instance of\nthe `Database` class once extending from the `Model` class.\n\nHere is an example of how `ProductModel` uses the `Database` methods.\n\n```php\n    // app/models/ProductModel.php\n\n    /**\n     * Fetches all products from the database.\n     *\n     * Retrieves all product data from the specified database table,\n     * ordered by productID in descending order.\n     *\n     * @return array An array containing Product objects representing all products in the database.\n     */\n    public function fetchAll(): array {\n        // Declare SQL\n        $sql = \"SELECT * FROM $this-\u003etable ORDER BY productID DESC\";\n        \n        // Query DB for data\n        $query = $this-\u003edb-\u003equery($sql);\n        \n        // Create product obj from result\n        $results = [];\n        while ($row = $query-\u003efetch_object(Product::class)) {\n            $results[] = $row;\n        }\n        \n        // Return list of Product objects\n        return $results;\n    }\n```\n\nIn this example, the `db` property, which contains the `Database` instance, calls the `query` method with a prepared SQL\nstatement.\n\nLet's see exactly how the `Database` object handles that request:\n\n```php\n    // app/core/Database.php\n\n    /**\n     * Send a query to the database.\n     *\n     * @param string $sql The SQL query to execute.\n     * @return bool|mysqli_result The result of the query.\n     * @throws mysqli_sql_exception if unable to execute the query.\n     */\n    public function query(string $sql): mysqli_result|bool {\n        try {\n            // Execute the query\n            return $this-\u003econnection-\u003equery($sql);\n        } catch (mysqli_sql_exception $e) {\n            // Handle query execution errors\n            ExceptionHandler::handleException($e, \"Unable to process the query made to our data services.\");\n        }\n    }\n```\n\nThe `Database` object will attempt to run the SQL query and return the results. PHP's `mysqli` object is used to connect\nto the database. It has a built-in exception named `mysqli_sql_exception`. Any errors or exceptions in database\noperations will throw this exception. This is an example of a low-level exception and is handled abruptly with the user\ncreated class `ExceptionHandler`.\n\nWe'll dive into exception handling in a later chapter! For now, let's move onto how views work within the framework.\n\n## Views\n\nViews are the client-oriented layer that provide an interface for interaction. They are the result of the backend work\ndone by the heavy lifting of Models and Controllers. The View layer contain action elements that directly call\ncontrollers in order to complete the events the user requests.\n\n### Declaration\n\nTo declare a view:\n\n1. Create a new PHP file in the `views/` directory.\n2. Define a class extending the base View class.\n3. Implement static methods that orchestrate HTML elements.\n\nExample:\n\n```php\n    // app/views/error/ErrorView.php\n\nclass ErrorView extends View {\n    /**\n     * Renders the error view.\n     *\n     * This method outputs the HTML content for displaying an error message.\n     * It includes the error message within a styled container.\n     *\n     * @param string $message The error message to display.\n     * @return void\n     */\n    public static function render(string $message): void {\n        // HTML for error page\n        ?\u003e\n        \u003c!DOCTYPE html\u003e\n        \u003chtml lang=\"en\"\u003e\n        \u003chead\u003e\n            \u003cmeta charset=\"UTF-8\"\u003e\n            \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n            \u003c!-- Favicon--\u003e\n            \u003clink rel=\"icon\" type=\"image/x-icon\" href=\"\u003c?=IMG_URL?\u003e/assets/favicon.ico\"/\u003e\n            \u003ctitle\u003eFitFlex: \u003c?= $message ?\u003e\u003c/title\u003e\n            \u003cstyle\u003e\n                body {\n                    font-family: Arial, sans-serif;\n                    margin: 0;\n                    padding: 0;\n                    background-color: #f4f4f4;\n                }\n\n                .container {\n                    max-width: 600px;\n                    margin: 50px auto;\n                    background-color: #fff;\n                    padding: 20px;\n                    border-radius: 5px;\n                    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n                }\n\n                h1 {\n                    color: #e74c3c;\n                }\n            \u003c/style\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n        \u003cdiv class=\"container\"\u003e\n            \u003ch1\u003eError\u003c/h1\u003e\n            \u003cp\u003e\u003c?php echo htmlspecialchars($message); ?\u003e\u003c/p\u003e\n            \u003cp\u003e\u003ca href=\"\u003c?= BASE_URL ?\u003e\"\u003eBack to Home\u003c/a\u003e\u003c/p\u003e\n        \u003c/div\u003e\n        \u003c/body\u003e\n        \u003c/html\u003e\n        \u003c?php\n    }\n}\n```\n\nOur example represents a view that displays an error message to the user. The static method `render` contains HTML/CSS\ncode formatting the page.\n\nWe also see PHP logic also incorporated into the method. The View layer is unique in that there should be minimal\nbusiness logic on each view. The model layer handles data processes and the controller layer handles actions and data\norganization.\n\n#### How does that implementation work?\n\n```php\n    public static function render(string $message): void {\n        // HTML for error page\n        ?\u003e\n```\n\nIn our instance, the `render` method accepts one argument:\n\n- `$message` - a string representing an error message that is displayed on the page.\n\n```php\n        \u003cdiv class=\"container\"\u003e\n            \u003ch1\u003eError\u003c/h1\u003e\n            \u003cp\u003e\u003c?php echo htmlspecialchars($message); ?\u003e\u003c/p\u003e\n            \u003cp\u003e\u003ca href=\"\u003c?= BASE_URL ?\u003e\"\u003eBack to Home\u003c/a\u003e\u003c/p\u003e\n        \u003c/div\u003e\n```\n\nDeeper into the method declaration, the `$message` variable is echoed out onto the web page, providing the user with the\nerror information.\n\n#### Where is the render method called from?\n\nView methods can be called from anywhere; however, they are primarily called from the controller layer.\n\nThis example has a dedicated controller that handles errors.\n\n```php\n    // app/controllers/ErrorController.php\n\n/**\n * Controller responsible for displaying error messages.\n */\nclass ErrorController {\n    public function display(): void {\n        // Render the error page\n        ErrorBaseView::render($message);\n    }\n}\n```\n\nDue to the static nature of the `ErrorView` class, its `render` method can be called from anywhere without instantiation\nof an `ErrorView` object. This allows flexibility in the specific use cases that the view provides. Any controller can\ndisplay an error page to the user without jumping through extra hoops.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjvspeed74%2Fphp-web-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjvspeed74%2Fphp-web-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjvspeed74%2Fphp-web-framework/lists"}