cloude / framework
Minimalist PHP 8.3+ micro-framework: router, input, views, markdown and string helpers. No magic, no database, no container. Designed for small and medium web projects running on Apache.
Requires
- php: >=8.3
Requires (Dev)
- erusev/parsedown: ^1.7
- friendsofphp/php-cs-fixer: ^3.60
- jbroadway/urlify: dev-master
- phpunit/phpunit: ^11.0
Suggests
- erusev/parsedown: Required to use Cloude\Markdown.
- jbroadway/urlify: Improves slug transliteration in Cloude\Str::slug() (an iconv-based fallback is used when missing).
README
A minimalist PHP micro-framework. No magic, no service container, no database. Designed for small and medium web projects running on Apache + PHP.
- PHP 8.3+
- PSR-4 autoloading, namespace
Cloude\ - PSR-12 / PER-CS 2.0 coding style
declare(strict_types=1)everywhere- Zero required runtime dependencies (Parsedown and URLify are optional)
Installation
composer require cloude/framework
Repository layout
cloude-php-workspace/
src/ # Framework source (PSR-4: Cloude\)
Input.php
Router.php
View.php
Str.php
Markdown.php
tests/ # PHPUnit tests
example/ # Runnable sample app (see example/README.md)
composer.json # Package manifest (name: cloude/framework)
phpunit.xml.dist
.php-cs-fixer.dist.php
LICENSE
README.md
Components
| Class | Responsibility |
|---|---|
Cloude\Router |
Router with /{param} patterns and get/post/put/patch/delete/any helpers |
Cloude\Input |
Wrapper over $_GET, $_POST, $_SERVER, raw body and JSON |
Cloude\View |
Plain PHP template rendering with variable extraction and HTML escape |
Cloude\Markdown |
Markdown parser with YAML frontmatter (requires erusev/parsedown) |
Cloude\Str |
Utilities: upTo(), truncate(), slug() |
Quick start
<?php declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; use Cloude\Input; use Cloude\Router; use Cloude\View; View::setBasePath(__DIR__ . '/views'); $router = new Router(); $router->get('/', function (): void { View::render('home.php', ['title' => 'Hello']); }); $router->get('/users/{id}', function (array $params): void { echo 'User #' . $params['id']; }); $router->post('/api/echo', function (): void { header('Content-Type: application/json'); echo json_encode(Input::json()); }); $router->setNotFound(function (): void { View::render('404.php'); }); $router->dispatch();
Example project
A complete, ready-to-run project lives in example/:
cd example
composer install
php -S localhost:8000 -t www
Open http://localhost:8000.
The example ships:
example/www/index.php- entry point and bootstrapexample/www/.htaccess- Apache rewrite rulesexample/app/config.php- base configurationexample/app/routes.php- route definitionsexample/views/- layout and pages
Class reference
Cloude\Router
$router = new Router(basePath: '/api'); // optional, stripped from the URI $router->get('/users/{id}', $handler); // GET $router->post('/users', $handler); // POST $router->any('/*', $handler); // any method $router->add(['/foo', '/bar'], $handler); // same handler, multiple routes $router->setNotFound(fn() => ...); // custom 404 $router->dispatch();
{name} segments are extracted into an associative array and passed as the first handler argument.
Cloude\Input
Input::method(); // GET, POST, ... Input::uri(); // path without query string, no double slashes Input::get('q'); // $_GET['q'] or null Input::post('name'); // $_POST['name'] or null Input::json(); // decodes JSON body into an array Input::body(); // raw request body Input::header('User-Agent'); Input::ip(trustProxy: false);
Cloude\View
View::setBasePath(__DIR__ . '/views'); View::render('home.php', ['title' => 'Hello']); // prints $html = View::capture('home.php', $vars); // returns a string echo View::e($text); // HTML escape
Cloude\Markdown
$result = Markdown::parse($markdownContent); // => ['meta' => [...], 'html' => '...', 'description' => '...', ...] $html = Markdown::toHtml($md);
Supports minimal YAML frontmatter (single-line key: value pairs):
--- title: My article description: Short summary --- # Body...
Cloude\Str
Str::upTo('hello world', ' '); // 'hello' Str::truncate('long text', 4); // 'long...' Str::slug('Hello World'); // 'hello-world'
Development
composer install composer test # phpunit composer cs-check # php-cs-fixer in dry-run mode composer cs-fix # apply fixes
Publishing to Packagist
-
Push this repository to GitHub as a public repo.
-
Submit the repository URL at https://packagist.org/packages/submit.
-
(Recommended) Configure the GitHub -> Packagist webhook so new tags are picked up automatically.
-
Tag a release:
git tag -a v0.1.0 -m "Initial release" git push origin v0.1.0
After publication, any project can install it with:
composer require cloude/framework
Philosophy
- No magic: the code you read is the code that runs. No generators, annotations or proxies.
- Small classes: each class fits in a file you can read in one sitting.
- No required dependencies: the core pulls nothing in. Parsedown and URLify are optional.
- No global state: no container, no singletons. Static classes are just namespaces for functions.
License
MIT - see LICENSE.