MeowBase2 is a lightweight PHP framework for web and CLI. It is modular and explicit about dependencies: a PHP-DI container owns object lifetimes, MeowBase bootstraps Config, SysLog, Profiler, and (for web) Request / Router / Response, and application data lives under meowbase-data/.
The design avoids hidden globals and deep inheritance. Services are composed, configured from PHP files, and hooked through events so applications stay in control of when models run and responses are sent.
Composer maps Paheon\MeowBase2\ to meowbase/. Application config and runtime data live under meowbase-data/. Local-only files (vendor/, composer.lock, cache/log contents, myconfig.php overrides) are not committed.
.
├── index.php Web front controller
├── composer.json Dependencies and PSR-4 autoload
├── phpunit.xml PHPUnit configuration
├── PathConfig.php Optional override for the data-directory path
├── ht.access Apache rewrite sample (live `.htaccess` is local-only)
├── LICENSE
├── ChangeLog
├── README.md Readme file (This file)
├── README_zh-Hant.md Readme file (Chinese version)
├── .gitignore
├── bin/ CLI entry points
│ ├── .htaccess use .htacces to Prevent web access
│ ├── queue.php Queue worker / dispatcher
│ ├── queueStatus.php Queue status monitor
│ ├── PathConfig.php
│ ├── RunAllTest.bat Run all PHPUnit tests (Windows version)
│ └── RunAllTest.sh Run all PHPUnit tests (Linux version)
├── meowbase/ Framework source
│ ├── MeowBase.php Application hub
│ ├── Config.php
│ ├── Container.php
│ ├── API/ JsonAPI client/server helper
│ ├── Cache/
│ ├── CachedDB/
│ ├── Controller/ Controller base + ExceptionController
│ ├── CsvDB/
│ ├── DataSource/
│ ├── DTree/
│ ├── HTML/ Request / Response
│ ├── Mail/
│ ├── ProcessPool/
│ ├── Profiler/
│ ├── Queue/ Envelopes, workers, transports
│ ├── Router/ Router, hops, handlers, RouteException
│ ├── SysLog/
│ ├── Tools/
│ ├── Trait/ PropertyCtrl, EventList, ErrorCtrl
│ ├── UserCtrl/
│ └── View/
├── meowbase-data/ Versioned config + empty runtime dirs
│ ├── .htaccess use .htacces to Prevent web access
│ ├── config/ Config file configs (`000-*.php` per component for default configuration)
│ ├── cache/ Placeholders (`.gitkeep` only)
│ ├── db/ File based DB storage
│ ├── log/ Log files
│ ├── spool/ Spools
│ └── tmp/ Temp. folder
├── docs/user-guide/ User guides (English and 正體中文)
├── example/ Sample programs (`demo.php`, `sendmail.php`)
└── tests/ PHPUnit tests and fixtures
Core is the bootstrap layer. Container (PHP-DI) creates shared or transient services instead of singletons scattered across the codebase. Config scans meowbase-data/config/, merges PHP files, and exposes values with slash-path access. MeowBase is the hub: it loads config, attaches SysLog and Profiler, and in web mode prepares Request, Router, and Response.
webRun() drives the web lifecycle with events (onBeforeRouting, onAfterRouting, onBeforeController, onBeforeResponse, …). It calls Router::route(), instantiates the Controller with routeInfo, runs the named action, and response->send(). CLI scripts typically set webAccess = false and skip webRun().
JsonAPI is a thin, safe wrapper around PHP cURL for talking to HTTP JSON APIs and for serving JSON when MeowBase2 is the provider. The intent is less boilerplate: one object builds the URL, headers, timeouts, and SSL options, then splits the raw body from response headers.
On the server side, response() sets Content-Type: application/json and encodes the payload. Optional helpers (getUserApiKey(), compareApiKey()) match incoming keys without inventing a separate auth stack.
Cache wraps Symfony’s tag-aware adapters (files, array, Memcached, Redis) behind a small active-record style API. The design goals are isolation and predictability: keys are sanitized, entries are scoped with siteID tags so environments do not leak into each other, and groups of related items can be invalidated at once.
findItem() loads the current item; set(), tag(), expiresAfter(), and save() operate on that item. Use it for expensive queries (or let CachedDB do that automatically), API responses, or shared state across requests.
CachedDB is a cache-aside data layer with one Medoo-style query API over SQL (CachedDBSQL) or CSV (CachedDBCSV). The point is to switch storage without rewriting accessors, and to cache reads without forgetting to clear them on writes.
Reads (select, get, has, count, …) build a deterministic cache key. On a miss they query the engine, store the result, and tag it with the table name. Mutations with $autoClear = true drop that table’s tagged entries so callers do not serve stale data.
DataSource unifies SQL, CSV (via CachedDB), JSON files, serialized arrays, and JSON APIs as arrays, with field mapping, validation, and calculated fields. Writes are optional per adapter. There is no factory in v1: construct one DataSource per resource with a MeowBase instance, an adapter, and a schema.
CsvDB treats a CSV file as an in-memory table: headers, built-in csvRowID / csvCreate / csvUpdate, search, and Medoo-style orderBy. It exists so small datasets can live on disk without a SQL server, and so CachedDBCSV has a real engine underneath.
Direct load() / setRow() / save() is fine for single-process tools. Concurrent web writes should queueAppend / queueUpdate / queueDelete then runQueue(), which takes an exclusive file lock, applies the batch, and rewrites the file so overlapping requests do not clobber each other.
DTree is an in-memory tree for hierarchical data — menus, directories, org charts — when a flat array is not enough. Nodes hold parent/child links and arbitrary data; paths use / (absolute from the root, relative from the current node). Iterators walk DFS or BFS, globally or on a subtree.
Serialization can include an HMAC so trees stored in sessions or cookies cannot be tampered with unnoticed. Prefer a plain array for lists; use DTree when navigation, ordered traversal, and move/copy of nodes matter.
The HTML group replaces raw $_SERVER, header(), and echo with Request and Response. Request normalizes method, host, path, query, and IP into a consistent info array. Response prepares status, content type, headers, and body for HTML, JSON, file download, or redirect, then send() delivers it.
These objects are created when webAccess is on. Router matches against Request; MeowBase::webRun() calls Response::send() after the controller action.
Mail wraps PHPMailer (Mailer) for outbound mail and Webklex PHPIMAP (MailBox) for inbound IMAP. Factories pull settings from config so send/receive stay testable and out of global state.
Synchronous send is SMTP / Sendmail / mail(). With async, Mailer queues mail.send fire-and-forget jobs so the web request returns quickly; CLI or ProcessPool workers (bin/queue.php) deliver in the background. MailBox switches IMAP accounts and folders when the app must read, parse, or process incoming mail.
Model is the base class for application logic used from Controllers. Subclasses get the MeowBase instance, PropertyCtrl for safe properties, and ErrorCtrl for consistent errors.
Routing resolves a Controller, not a Model. Controllers may construct and call $model->run(). The default run() fires onModelRunBegin and onModelRunEnd for hooks.
ProcessPool is a small, OS-agnostic supervisor for PHP child processes. It is generic: children are launched with a worker flag (default --single) and report load via exit codes. It does not embed a queue implementation.
The pool keeps workerMin standby workers (restarted if they die) and burst-scales with EXIT_HAD_WORK (0) to grow and EXIT_IDLE (10) or other codes to shrink, up to workerMax. An idle pool periodically probes with one burst worker. Pipes are always drained; logChildOutput can log stdout/stderr on exit.
Profiler measures where time goes: you record() named tags, then generate a report of duration and share between tags. The default Profiler keeps records in memory (low overhead). ProfilerFile writes as it goes, for long or memory-heavy runs where an in-memory log would grow too large (file I/O slightly affects the numbers).
ProfilerFactory builds the driver from config. Typical use is a start timestamp in index.php or demo.php, tags around expensive work, then a report at the end of the request.
Queue splits transport, dispatch, and processing so the same job shape works in-process or across machines. Every job is a QueueEnvelope (id, type, service, payload, stamps). Transports include memory (tests), file, APCu (shm), Memcached, Redis, and a socket broker.
QueueDispatcher produces: fire-and-forget, RPC-style ask / waitForResponse, or non-blocking onResponse + tick(). QueueReceiver plus QueueWorker consume, run handlers (registered or from the container), and optionally reply on the response channel. CLI is unified as bin/queue.php (omit --type to use Config Queue.driver; includes mail.send via MailSendHandler). Optional apiKey seals envelope data with AES-256-GCM; routing metadata stays cleartext.
Router turns an HTTP request into a controller recipe {controller, action, routeInfo} by walking registered hops until the first non-SKIP result. MeowBase::webRun() instantiates the Controller and calls the named action.
If nothing matches or a handler returns a terminal int, RouteException::handle() supplies a fallback recipe (404, error). Optional route cache skips the hop chain for hot, stable URLs; lookup and save happen inside route().
SysLog is PSR-3 logging on Katzgrau KLogger, plus optional backtraces so you can see where a line was written. Standard methods (info, debug, error, …) log without a trace; sysLog(), sysLogContext(), and log() can prepend or append stack data according to config. A threshold keeps noise off disk.
SysLogFactory reads the Log section of config. The live instance is $meow->log after bootstrap — you do not construct it by hand in normal use.
Tools are independent utilities, not a pipeline. The design is to keep path, URL, MIME, password, and environment helpers out of the global namespace and out of copy-pasted regex.
- File — directories, unique temp files, recursive remove, ZIP create/extract
- Url — parse/compose, relative ↔ absolute against a home URL
- Mime — types from extension or
finfo, plus aliases - Password — generate by strength rules; validate and hash
- PHPTools — CLI vs web, disabled functions, session info, class dump
Traits are how MeowBase2 shares behavior without a deep class tree. Any class can use them and stay small.
- PropertyCtrl —
__get/__setwith deny lists, mapping, mass get/set (opSuccess), and slash-path access into nested arrays - EventList — synchronous named events (register, trigger, unregister) for lifecycle hooks
- ErrorCtrl —
lastErrorplus optionalthrowException()with a chosen exception class
Use these when building models or services that should feel like the rest of the framework.
UserCtrl is authentication, groups, and permissions on top of CachedDB (SQL or CSV). Storage is split (User, UserGroup, UserPerm); UserManager is the façade for session, login, password checks, and RBAC so callers do not care whether a permission came from the user or a group.
Sessions work in web and CLI. Use it when the app needs login, single-session enforcement, or item-level permissions without tying that logic to one database engine.
License: MIT