Scriptlog is a personal blogging platform built on PHP 7.4+ and MySQL/MariaDB. Before you write your first line of code, whether you are extending the platform, building a plugin, or just debugging an issue, you need to know how the project is laid out and how the pieces relate to each other. This article covers exactly that.
The Root Directory
When you clone the repository, you land in the project root. A handful of files live here directly:
index.phpis the public front controller. Every request to the blog passes through this file first.config.phpis the main configuration file. It returns an associative array covering database credentials, application settings, mail/SMTP options, and the path to the Defuse encryption key..envis the environment file thatconfig.phpreads from. During installation both files are generated and kept in sync. In production you keep secrets in.envand commit onlyconfig.php.composer.jsonis the dependency manifest. Runcomposer installhere before touching anything else.
Nothing else belongs in the root. If you find yourself creating files here, stop and reconsider.
admin/
The admin panel is a self-contained area with its own entry point at admin/index.php and its own login page at admin/login.php. Every page under this directory is protected by session authentication through the SessionMaker class. The admin panel handles posts, pages, media, users, plugins, themes, navigation, and site configuration. You will rarely need to modify files here directly unless you are customising the admin UI itself.
api/
The REST API lives under api/ with a single entry point at api/index.php. All API routes follow the convention api/v1/{resource}. The API handles its own routing independently from the main blog front controller. Authentication for API endpoints is separate from the admin session. See the API Documentation for details on endpoints and authentication tokens.
lib/ - The Core Library
This is where the majority of the codebase lives, and it is the directory you will spend the most time in as a developer.
lib/main.php and lib/common.php
main.php is the application bootstrap loader. common.php defines global constants and utility functions used across the entire application. Both are loaded early in the request lifecycle.
lib/core/
The engine of Scriptlog. Key files here include:
Bootstrap.phpinitialises the application, registers routes, and dispatches requests.Dispatcher.phpmaps an incoming route to the correct controller.View.phphandles template rendering and passes data to theme files.DbFactory.phpcreates and manages PDO database connections.Authentication.phpmanages login state, session fingerprinting, and remember-me token encryption.Session.phpis the custom session handler known asSessionMaker.
lib/core/dao/
Data Access Objects. Every database table has a corresponding DAO class. DAOs are the only place where SQL queries are written. They accept plain PHP values, execute prepared statements, and return data as arrays or model objects. Writing raw queries outside a DAO is not allowed. This is a hard architectural rule, not a suggestion.
lib/core/service/
Business logic. Services sit between controllers and DAOs. A service method might call one or more DAO methods, apply validation, fire hooks, or transform data before returning a result. If you are adding a feature, your logic belongs here, not in a controller or a DAO.
lib/core/controller/
Request controllers. Each controller corresponds to a route or a group of routes. Controllers receive the HTTP request, call the appropriate service method, and pass data to the view. They should contain no business logic and no SQL. Their job is coordination only.
lib/core/model/
Data models. Models are plain value objects that represent a row or a resource. They carry data between layers without any knowledge of the database or the HTTP request.
lib/core/utility/
A large collection of standalone utility files, over 100 in total. These cover image processing, encryption and decryption, CSRF token generation and verification, XSS sanitisation, slug generation, pagination, date formatting, upload handling, and more. When you need a helper function, look here before writing your own.
public/
This is the web root. Your web server (Apache or Nginx) should point its document root here, not to the project root.
public/themes/
Themes live here. The default theme is public/themes/blog/. Each theme is a directory containing PHP template files and its own assets (CSS, JS, images). The View.php class resolves template paths relative to the active theme directory. To build a custom theme, copy the default theme directory, rename it, and register it through the admin panel.
public/files/
All user-uploaded content goes here, organised into subdirectories: pictures/, audio/, video/, and docs/. The upload utility creates multiple image sizes plus a WebP variant for each uploaded image. Web users should never be able to write outside this directory.
public/cache/ and public/log/
The cache directory stores compiled or cached output. The log directory stores application logs. Both need to be writable by the web server user. Set permissions to 755 for both during installation.
install/
The installation wizard. This directory contains the three-step setup flow: requirements check (index.php), database setup (setup-db.php), and finalisation (finish.php). It also contains include/dbtable.php, which defines all 21 database table schemas. Delete this entire directory as soon as installation is complete. Leaving it in place is a security risk.
tests/
PHPUnit test suite. Unit tests live under tests/unit/. Each feature area has its own test file. Run the suite with vendor/bin/phpunit from the project root. Static analysis runs separately via vendor/bin/phpstan. Both tools are configured in the project root.
docs/
Developer documentation as Markdown files. Key documents include DEVELOPER_GUIDE.md, TESTING_GUIDE.md, PLUGIN_DEVELOPER_GUIDE.md, and API_DOCUMENTATION.md. The OpenAPI specification for the REST API is available as both API_OPENAPI.yaml and API_OPENAPI.json.
The Request Lifecycle in Context
Understanding the directory structure makes more sense when you see how a request actually flows through it:
public/index.php
-> lib/main.php (bootstrap)
-> lib/core/Bootstrap.php (routing)
-> lib/core/Dispatcher.php (dispatch)
-> lib/core/controller/*.php (handle request)
-> lib/core/service/*.php (business logic)
-> lib/core/dao/*.php (database)
-> public/themes/blog/*.php (render view)
Each layer has one job. A request enters through the public front controller, gets routed, hits a controller that delegates to a service, the service queries the database through a DAO, and the result is rendered by a theme template. Nothing skips a layer.
Summary
Scriptlog keeps a clear separation between public-facing files (public/), the admin panel (admin/), the API (api/), and the core library (lib/). The library enforces a layered architecture where each directory (dao/, service/, controller/, model/, utility/) has a bounded and specific role. Getting this layout into your head first is the foundation for everything else: adding features, writing tests, building themes, and tracking down bugs.
Comments (11)
Leave a Comment