diff --git a/.gitignore b/.gitignore index 8c12c70..a5b4848 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ sessions/* ignore -kanban/ -.pi-tasks diff --git a/README.md b/README.md index 36f1b16..b539f72 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Revival of the old ZapMachine. A collage machine that merges random images layer by layer remaining about 50% of the original images - -## ApFab - Zap Machine V0.9.0 - +######################################################## +#### ApFab - Zap Machine V0.9.0 ### +######################################################## π― 2009 - 2026 Fabian de Boer, Vincent Bruijne www.apfab.com @@ -28,7 +28,21 @@ You need to acquire the following API key and put it into `conf/conf.php`: See `conf/conf.php` for the exact constant names to use. -Adjust permission (chmod 777) for 'sessions' folder, run index.php in the www/ directory, fill in the form and voila. +Adjust permission (chmod 777) for Sessions folder, run index.html, fill in the form and voila. + + + +# how it works + +- index.html contains user interface +- user input put into Session variables in init.php +- display.inc.php dynamically displays two img tags that are loaded in turn with image data from zap.php using display.js +- zap.php downloads images one by one using imgSearch Class +- zap.php processes collages one by one using imgProcess Class +- All downloaded images, collage images and logfiles(feature is far from ready yet) are put in a folder named 'Session/zap'.date() +- the machine is ready when an error is loaded into img document class. (a weak point in our construction) + + # yet to come - Logfile also generated in imgProcess Class (0.9.2) diff --git a/docs/framework_guide.md b/docs/framework_guide.md deleted file mode 100644 index 4bff8d8..0000000 --- a/docs/framework_guide.md +++ /dev/null @@ -1,479 +0,0 @@ -# ZAP Framework Guide - -This document explains the internal architecture of ZapMachine and provides stepβbyβstep instructions for extending it with new handlers, templates, and interactive elements. - -## Architecture Overview - -### Front-Controller Routing (`?mode=X` β `ZAPController` β `ZAP{Mode}`) - -All HTTP requests enter through `www/index.php`. The front controller reads the `mode` query parameter and dispatches to a class whose name is formed as `ZAP` + the ucfirst'd mode value. - -```php -// www/index.php (simplified) -$mode = isset($_GET['mode']) ? $_GET['mode'] : 'home'; -require_once '..' . DIRECTORY_SEPARATOR . 'conf' . DIRECTORY_SEPARATOR . 'init.php'; -$config = Config::getInstance(); -$config->register('engines', $search_engines); -$frontcontroller = new ZAPController($mode); -$output = $frontcontroller->fetch(); -print($output); -``` - -`ZAPController` (in `lib/ZAPController.class.php`) constructs the class name from a prefix (`ZAP`) and the mode, verifies it exists via the autoloader, instantiates it, and calls `getContent()`: - -```php -class ZAPController { - private $prefix = 'ZAP'; - - public function __construct($mode = 'home') { - $mode = trim(strip_tags($mode)); - $this->mode = ucfirst($mode); - $clazz = $this->prefix . $this->mode; - if (!class_exists($clazz)) { - die('Wrong parameter'); - } - $this->action = new $clazz(); - } - - public function fetch() { - return $this->action->getContent(); - } -} -``` - -**Registered modes and their handlers:** - -| `?mode=` | Class | Template | Purpose | -|----------|-------|----------|---------| -| `home` (default) | `ZAPHome` | `tpl/index.tpl` | Word-input form | -| `display` | `ZAPDisplay` | `tpl/display.tpl` | Collage-generation progress screen | -| `zap` | `ZAPZap` | *(raw image output)* | Downloads images & processes collages, returns image blobs | - ---- - -### Autoloader - -`conf/init.php` registers an `spl_autoload_register` callback that searches every directory on the include path for both `{ClassName}.class.php` and `{ClassName}.interface.php`: - -```php -spl_autoload_register(function ($class_name) { - $include_path_tokens = explode(PATH_SEPARATOR, get_include_path()); - foreach ($include_path_tokens as $prefix) { - $path[0] = $prefix . DIRECTORY_SEPARATOR . $class_name . '.interface.php'; - $path[1] = $prefix . DIRECTORY_SEPARATOR . $class_name . '.class.php'; - foreach ($path as $thisPath) { - if (file_exists($thisPath)) { - require_once $thisPath; - return; - } - } - } -}); -``` - -The include path is built from these directories (relative to the project root): - -| Constant | Directory | -|----------|-----------| -| `ZAP_RESOURCE_DIR` | `resource/` β search-engine implementations | -| `ZAP_UTILS_DIR` | `utils/` β utility classes (Session, ZAPCurl, Log) | -| `ZAP_SESSIONS_DIR` | `sessions/` | -| `ZAP_LIB_DIR` | `lib/` β core handlers and image processing | -| `ZAP_TPL_DIR` | `tpl/` β templates | -| `ZAP_CONF_DIR` | `conf/` β configuration | - -This means any `.class.php` or `.interface.php` file placed in one of those directories is automatically found β no manual `require_once` calls needed for library classes. - ---- - -### Config Singleton - -Configuration values that must be accessible from any class are managed through `Config` (`conf/Config.class.php`), a classic singleton: - -```php -$config = Config::getInstance(); -$config->register('engines', $search_engines); // store -$engines = $config->retrieve('engines'); // retrieve β returns false if not found -``` - -The singleton is wired in `www/index.php` immediately after `init.php` is loaded. - ---- - -### Session Management - -The `Session` class (`utils/Session.class.php`) wraps PHP's native sessions: - -```php -Session::start('ZAP'); // Begin session (sessions named 'ZAP') -Session::reset(); // Clear $_SESSION array -Session::deleteCookie(); // Remove client-side session cookie -Session::destroy(); // Reset + delete cookie + session_destroy() -``` - -Session data is stored verbatim in `$_SESSION` and uses these keys: - -| Key | Type | Set by | Purpose | -|-----|------|--------|---------| -| `words` | `string[]` | `ZAPDisplay` | Keyword list for image search | -| `folder` | `string` | `ZAPDisplay` | Absolute path to session folder | -| `collages` | `int` | `ZAPDisplay` | Number of collages to generate | -| `width` | `int` | `ZAPDisplay` | Collage width in pixels | -| `height` | `int` | `ZAPDisplay` | Collage height in pixels | -| `images` | `string[]` | `ZAPZap` | Queue of source image paths for current collage | -| `imgOriginal` | `string[]` | `ZAPZap` | Immutable copy of the original image list | -| `collageNr` | `int` | `ZAPZap` | Current collage sequence number | - -**CLI note:** Session is only started automatically in web mode. See [CLI Bootstrap](#cli-bootstrap) below. - ---- - -### Plugin-Based Search Engines - -Search engines implement the `Search` interface (`resource/Search.interface.php`): - -```php -interface Search { - public function setQuery($word); - public function setParam($key, $value); - public function getData(); -} -``` - -Each engine class lives in `resource/` and returns data in a universal array format: - -```php -$data['result'] = true; // Boolean: success / failure -$data['engine'] = __CLASS__; // Engine name, e.g. 'Brave' -$data['results'][0]['clickurl'] = '...'; // Image URL to download -$data['results'][0]['referer'] = '...'; // Source page URL -$data['results'][0]['thumb'] = '...'; // Thumbnail URL -``` - -Available engines are registered as an array of names in `conf/conf.php` and wired through the Config singleton at runtime. `ImgSearch` randomly selects an engine from the registered list each time it runs: - -```php -$config = Config::getInstance(); -$engines = $config->retrieve('engines'); -$this->engine = $engines[array_rand($engines)]; -``` - -**Current engine:** `Brave` (`resource/Brave.class.php`) β uses the Brave Search API. - -To add a new engine: -1. Create `resource/YourEngine.class.php` implementing `Search`. -2. Add `'yourengine'` to the `$search_engines` array in `conf/conf.php`. - ---- - -### Image Processing Pipeline - -The image pipeline runs entirely server-side through three classes in `lib/`: - -1. **`ImgSearch`** (`lib/ImgSearch.class.php`) β searches for an image URL via a registered engine, downloads the file, validates its MIME type (`image/jpeg`, `image/gif`, `image/png`), and saves it to the session folder. - -2. **`ImgProcess`** (`lib/ImgProcess.class.php`) β constructs collages using ImageMagick. Each collage starts with `createBasicCanvas()` (zoom-crops the first source image) then layers additional images via `addToCanvas()` (random transparency + composite). - -3. **`ImgIOTools`** (`lib/ImgIOTools.class.php`) β handles output: `onScreen()` streams an Imagick object as an HTTP image response; `urlOnScreen()` loads a file path first; `saveImg()` writes to disk. - -4. **`ImgTools`** (`lib/ImgTools.class.php`) β utility functions: `zoomCrop()`, `transparantRandom()`, `randomRotate()`, `resize()`, `recolor()`, `convertGifsToPng()`. - ---- - -### CLI Bootstrap - -CLI scripts (in `cli_scripts/`) bootstrap the application differently from web requests. Because `init.php` skips `Session::start()` when `php_sapi_name() === 'cli'`, CLI scripts must handle sessions manually if needed. - -**Example β `cli_scripts/test.php`:** - -```php -#!/usr/bin/env php -register('engines', $search_engines); -``` - -Key differences from the web entry point: -- `chdir(__DIR__)` is **required** before `require_once 'init.php'` because `init.php` uses `realpath('.')` to define `ZAP_APP_BASE_DIR`. -- `Session::start()` is **not called** β the `init.php` condition `php_sapi_name() !== 'cli'` guards against it. -- You must explicitly register engines with `Config::getInstance()->register('engines', β¦)` because `www/index.php` normally does this. - ---- - -### Logging - -The `Log` class (`utils/Log.class.php`) writes timestamped messages to a session folder. It is used by `ImgSearch` (controlled by the `ZAP_LOG` constant in `conf/conf.php`). Each log call appends to both `log.txt` (plain text) and `log.xml` (XML-friendly) in the session's output directory. - -```php -if (ZAP_LOG == true) { - $log = new Log($this->folder); - $log->addToLog('ok', 'Search: ', "downloaded to $imageName"); - $log->addToLog('error', 'Search: ', 'bad search data received'); -} -``` - ---- - -## Template Rendering Pipeline - -The template system is a minimal PHP-native pattern built around `ob_start()` / `include` / `extract()` / `ob_get_clean()`. There is no template engine β templates are plain PHP files that receive variables via the symbol table. - -### How Templates Work - -Every `ZAP{Mode}` handler follows the same rendering pattern: - -```php -class ZAPHome { - private $template = 'index'; // corresponds to tpl/index.tpl - private $tpl = ''; - - public function __construct() { - $this->ver = ZAP_VERSION; - extract(get_object_vars($this)); // β makes all private properties available as $variables - - ob_start(); // β start output buffer - include getZAPTemplate($this->template); // β include the .tpl file - $this->tpl .= ob_get_clean(); // β capture buffer content, clean buffer - } - - public function getContent() { - return $this->tpl; - } -} -``` - -**Step by step:** - -1. Properties are set in the constructor (often from `$_POST`, `$_SESSION`, or constants like `ZAP_VERSION`). -2. `extract(get_object_vars($this))` promotes every private property to a local variable in the current symbol table. A property `$this->template = 'index'` becomes `$template = 'index'`. -3. `ob_start()` begins capturing all output. -4. `include getZAPTemplate($this->template)` includes the file `tpl/{name}.tpl`. The included file inherits the current symbol table, so it can use `$ver`, `$folder`, `$words`, etc. directly. -5. `ob_get_clean()` retrieves the captured HTML and closes the buffer. -6. `getContent()` returns the accumulated output string, which `ZAPController::fetch()` returns to `index.php` for `print()`. - -### The `getZAPTemplate()` Helper - -```php -function getZAPTemplate($tplname) { - $tplfile = $tplname . '.tpl'; - $tplfilepath = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR - . ZAP_TPL_DIR . DIRECTORY_SEPARATOR . $tplfile; - if (file_exists($tplfilepath)) { - return $tplfilepath; - } else { - return ERROR_PREFIX . 'No template'; - } -} -``` - -All templates live in the `tpl/` directory and have the `.tpl` extension. The helper resolves the absolute path so the `include` call works from any working directory. - -### Adding a New Template - -To add a new page/view: - -1. **Create the `ZAP{Mode}` handler** in `lib/`. The class must be named `ZAP{Mode}` (case-sensitive, PascalCase after the prefix) and must implement a `getContent()` method that returns a string: - - ```php - pageTitle = 'My Example Page'; - extract(get_object_vars($this)); - - ob_start(); - include getZAPTemplate($this->template); - $this->tpl .= ob_get_clean(); - } - - public function getContent() { - return $this->tpl; - } - } - ``` - -2. **Create the template** at `tpl/example.tpl`: - - ```php - - -
ZAP version
- - - ``` - -3. **Use the route:** Navigate to `index.php?mode=example`. The autoloader finds `ZAPExample` in `lib/`, the front controller instantiates it, and `getZAPTemplate()` loads `tpl/example.tpl`. - -No registration step is required β the front controller's naming convention (`ZAP` + ucfirst(mode)) and the autoloader handle discovery automatically. - ---- - -## Adding Interactive Elements - -This section documents the two interactive-element patterns used in the application. Each pattern pairs a server-side template with a client-side JavaScript file. - -### Pattern 1: Form with Client-Side Validation (`index.tpl` + `js/main.js`) - -Used by `ZAPHome` to collect word inputs, validate them in the browser, and POST to the `display` mode. - -**Server side (`ZAPHome` in `lib/ZAPHome.class.php`):** - -The handler passes two template variables into `tpl/index.tpl`: -- `$ver` β the ZAP version string. -- `$status` β an optional status message from the query string (`?stat=...`). - -The template renders a `