Irakli Gzirishvili
Creator of PHPShiftPHPShift is the world's first AI-powered full-stack PHP framework, launched on December 19, 2024. It eliminates the traditional development grind by letting you describe what you want in plain language - and having AI generate production-ready PHP pages, REST APIs, cron jobs, translations, SEO markup and database schemas for you.
At its core, PHPShift is a CLI-driven development environment built on Python, wrapping a structured PHP project frame. You interact with it through a terminal menu; it handles localhost orchestration (XAMPP), database management (MySQL via SQLAlchemy), Git version control and AI code generation - all from one unified interface.
PHPShift is not a library you import. It is a project generator and manager: you run it alongside your project directory and it scaffolds, modifies and maintains your PHP web application through AI conversations and structured skill prompts.
Every generation action in PHPShift is called a Skill. Skills are categorized by intent: Create, Edit, Delete, Read, Render and More. Each Skill carries a carefully engineered AI prompt that understands your project's existing codebase, database schema and PHP method surface before producing any code.
A PHPShift-managed project contains these top-level elements:
PHPShift targets developers and technical builders who want to move fast on full-stack PHP projects without writing every line manually. It is ideal for:
PHPShift compresses what normally takes days of scaffolding, wiring and boilerplate into a single CLI session. Here is what that means in practice.
Every page, API and cron job is generated by an AI that already knows your project. Before it writes a single line, the AI receives:
AI-USE doc annotations)App object methodsThis context window means generated code is consistent with what already exists - not generic boilerplate pasted from the internet.
A single "Create Page" action produces all of these in one shot:
readme.md - page goal, functionality and usage summarypage.html - responsive HTML structure (no <html> or <head> - the framework injects those)style.css - scoped responsive stylesscript.js - jQuery/jQook interaction logiccode.php - a named PHP class handling all server-side business logicdb.sql - ready-to-execute MySQL migration (optionally applied immediately)config.json - page metadata (group, name, composer dependencies)You review the result, confirm or redo it and the patch system handles rollback if you decline.
PHPShift wraps every AI generation action in a patch system. Before writing anything, it snapshots current file states. If the output is wrong, one keypress rolls back every file change and every database mutation - as if the action never happened.
PHPShift automates XAMPP configuration. When you start a project, it:
httpd-vhosts.confhosts fileStopping the project reverses the process cleanly.
After every successful generation, PHPShift asks if you want to commit. It handles git add, git commit and optionally git push - keeping your history granular and meaningful without requiring you to leave the CLI.
PHPShift supports multiple AI providers. You can switch models mid-project using the More > ChangeAIModel skill. Google Vertex AI (with service account key files) and API-key-based models are both supported.
The translation skill adds multi-language support to any page in one step: it updates HTML attributes, CSS positioning, JavaScript App.lng() calls, PHP App::lng() calls and generates the translation JSON file - all from a single natural language request.
The generated .htaccess enforces:
.env, logs and storage pathsFramework-level security is not an afterthought. There is even a dedicated PHPShift > RefineSecurity skill that runs a penetration-expert AI analysis of your framework codebase and reports exploitable vulnerabilities.
PHPShift operates as a Python CLI application that sits beside your PHP project and orchestrates AI, localhost, database and version control through a terminal menu.
When you run phpshift start, the following happens:
.env to confirm it's configured with an AI model, API key, database credentials and local domain.setup.sql if new). You choose how to handle pre-existing databases: clear, keep, or rename.php cron -auto once per minute.When you select a skill and describe your task:
Collectors: schema, file list, PHP methods, JS methods, styling, etc.db.sql file was generated, you're shown it in VS Code and asked to confirm before it's applied to the database.Yes (keep changes and optionally commit), Redo (roll back and try again with an adjusted description), or No (roll back entirely)..md Project Briefing FileEvery PHPShift project has a hidden .md file at its root. This is your project briefing document - a structured markdown file describing the project name, audience, problem, solution, pages, APIs, cron jobs, usage flow and functionality notes.
The AI reads this file when generating or refining your project plan. You can edit it manually in VS Code at any time, or use the More > RefineIdea skill to have the AI generate or improve it based on a short description.
| Category | Purpose | |----------|---------| | Create | Generate new pages, APIs, cron jobs, translations, SEO, README, LICENSE | | Edit | Modify existing units using AI with full codebase context | | Delete | Remove pages, APIs, cron jobs, logs, translations and related files | | Read | AI-assisted database querying, git command generation, log analysis | | Render | Re-render an existing page or generate a new access group method | | More | Change AI model, manage cron start/stop, export DB schema, install Composer plugins | | PHPShift | Self-hosted: update PHPShift, view documentation, visit PHPShift site |
PHPShift enforces strict naming rules generated by AI:
edititem, dashboard)public, private, admin)v{version}.{camelCaseName}.php (e.g., v1.getItems.php)camelCase.php (e.g., updatePrices.php)Pages/{group}.{name}/ foldersBefore setting up a PHPShift project, make sure your environment meets the following requirements.
PHPShift is currently designed for Windows. The localhost management module directly interacts with:
C:/xampp/ - XAMPP installation pathC:/Windows/System32/drivers/etc/hosts - system hosts fileC:/xampp/apache/conf/extra/httpd-vhosts.conf - Apache virtual hosts.lnk files via win32com)Linux and macOS support would require modifications to the localhost management module.
| Software | Purpose |
|----------|---------|
| XAMPP | Local Apache + MySQL server (C:/xampp) |
| Python 3.x | PHPShift CLI runtime |
| PHP (via XAMPP) | Project execution |
| MySQL (via XAMPP) | Project database |
| VS Code | Default editor (PHPShift opens files in code) |
| Git | Version control (optional but recommended) |
PHPShift uses the following Python packages (installed alongside PHPShift):
clight - PHPShift's CLI rendering and input systemaisi - PHPShift's AI skill interface (prompt assembly + model communication)sqlalchemy + pymysql - Database ORM and MySQL driverwin32com - Windows shortcut creationpyyaml - YAML config parsingthreading, subprocess, secrets, shutil - Standard library utilitiesYou need one of the following:
aisi module (e.g., Claude, OpenAI-compatible endpoints). You supply the model identifier string and API key..env value PHPSHIFT_AIKEY must be the relative path to this file.You select and configure your AI model during phpshift new project setup and can change it later with More > ChangeAIModel.
PHPShift requires a running MySQL server (port 3306 by default, via XAMPP). During project creation, you provide:
localhost)The framework auto-runs setup.sql on first start to create system tables (user_files, user_keys, api).
PHPShift is distributed as a Python package via the clight ecosystem. The installation process sets up the PHPShift CLI command, its internal .system frame and all required Python dependencies.
Install PHPShift using pip:
pip install phpshift
This makes the phpshift command available globally in your terminal.
phpshift --version
You should see the current PHPShift version number.
Ensure XAMPP is installed at C:/xampp with both Apache and MySQL modules enabled. PHPShift looks for:
C:/xampp/xampp_start.exe
C:/xampp/apache/logs/httpd.pid
C:/xampp/mysql/data/mysql.pid
C:/xampp/apache/conf/extra/httpd-vhosts.conf
If XAMPP is already running when you start a PHPShift project, PHPShift will detect it and skip the start step.
PHPShift automatically opens files in VS Code using the code command. Make sure VS Code's CLI is in your system PATH:
Ctrl+Shift+P → type Shell Command: Install 'code' command in PATHPHPShift will open your .md briefing file, .env config and SQL migration files in VS Code automatically during the workflow.
After installation, navigate to an empty folder where you want your first project and run:
phpshift new
PHPShift will begin the interactive project creation process. See Getting Started → First Project for a full walkthrough.
To update PHPShift to the latest version:
phpshift update
Or via pip:
pip install --upgrade phpshift
The PHPShift > Update skill inside the development session also handles updates from within the tool.
This walkthrough takes you from an empty folder to a running PHPShift project with your first AI-generated page.
Open a terminal in your desired project directory and run:
phpshift new
PHPShift will prompt you for project details. These values are injected into your project's .env and initial files:
| Prompt | Example |
|--------|---------|
| Project name | My App |
| Project author | John Doe |
| Local domain | myapp.local |
| Production domain | myapp.com |
| Database host | localhost |
| Database name | myapp_db |
| Database user | root |
| Database password | (your MySQL root password) |
| AI model | (select from list) |
| AI key / key file | (your API key) |
After entering these, PHPShift:
APP_KEY.md plan)code .md so you can review your project planWhen prompted "Want to refine your idea?", type a short description of your project:
A task management app where users can create, assign and track tasks in teams
The AI generates a structured .md project plan. Review it in VS Code. If you like it, confirm - the plan is saved and will guide future AI generations. If not, it's rolled back.
When asked "Want to start development?", answer Yes. Or start later with:
phpshift start
PHPShift will: - Configure and start XAMPP (Apache + MySQL) - Add your local domain to the hosts file and Apache virtual hosts - Connect to the database (create it if needed) - Open your project in the browser - Launch the interactive skill menu
In the skill menu, select Create → Page, then describe your page:
A public landing page with a hero section, feature highlights and a call-to-action button that links to registration
PHPShift collects project context (schema, PHP methods, JS methods) and sends it to the AI. In a few seconds, it writes:
Pages/public.welcome/readme.mdPages/public.welcome/page.htmlPages/public.welcome/style.cssPages/public.welcome/script.jsPages/public.welcome/code.phpIf a database table is needed, db.sql appears in VS Code for your review before being applied.
The CLI asks: "Want to keep changes?"
Open your browser at http://myapp.local/welcome and see the generated page live. Iterate by selecting Edit → Page to refine it.
When done for the day:
phpshift stop
Or select the exit option in the skill menu. PHPShift removes the virtual host, updates the hosts file and stops XAMPP (if no other projects are running).
A PHPShift project has a well-defined directory layout. Understanding this structure helps you navigate the generated codebase and know what each part is responsible for.
my-project/
├── .assets/ # Public static assets served by Apache
│ ├── script.js # Global jQook/jQuery app logic
│ ├── style.css # Global base styles
│ ├── logo.png # Project logo
│ ├── default.png # Default image placeholder
│ ├── default.mp4 # Default video placeholder
│ ├── default.mp3 # Default audio placeholder
│ ├── robots.txt # Search engine crawl rules
│ ├── sitemap.xml # Auto-updated sitemap
│ └── background.webp # Background asset
├── .system/ # PHP framework engine (internal, not public)
│ ├── app.php # Router entry point (all requests route here via .htaccess)
│ ├── composer.json # Composer dependency manifest
│ └── vendor/ # Composer-installed PHP packages
├── Apis/ # PHP REST API files
│ └── v1.example.php
├── Crons/ # PHP cron job scripts
│ └── updateItems.php
├── Pages/ # Full-stack page units
│ ├── public.welcome/
│ │ ├── readme.md # Page documentation
│ │ ├── page.html # HTML structure
│ │ ├── style.css # Page styles
│ │ ├── script.js # Page JavaScript
│ │ └── code.php # PHP business logic class
│ └── private.dashboard/
│ └── ...
├── Space/ # Project-level data files
│ ├── seo.html # SEO meta tags (Open Graph, Twitter Card, etc.)
│ ├── lng.en.json # English translation keys (auto-generated)
│ ├── lng.ka.json # Georgian translation keys
│ └── *.log # Application log files
├── .access # PHP Access class - defines page access groups
├── .env # Environment configuration (never public)
├── .htaccess # Apache routing, security headers, rate limiting
├── .gitignore # Git ignore rules
├── .md # AI-readable project briefing (hidden file)
├── README.md # Project readme (AI-generated or manual)
├── LICENSE # Project license file
└── cron # PHP cron runner entry point
.system/ DirectoryThis is the internal PHP framework engine. It is not editable by AI generation - it is the stable base that powers routing, authentication, database ORM, API dispatch, cron execution and the App helper class.
Key PHP method families available to generated code:
App::env($key) - Read environment variableApp::done($message, $data) - Return successful JSON responseApp::failed($message) - Return error JSON responseApp::info($message, $data) - Return informational JSON responseApp::error($message) - Log internal developer errorApp::lng($key, $text, $vars) - Translate a stringApp::redirect($url) - Not used in generated code (handled by JS instead)All custom PHP classes in pages and APIs extend this surface through static method calls.
Each page lives in a folder named {group}.{name} inside Pages/. For example, a public dashboard page lives at Pages/public.dashboard/.
The group determines access control. The .access PHP class defines static methods named after groups:
class Access {
public static function private() {
// Return true if current user passes the 'private' group check
}
}
PHPShift generates group methods automatically if a new group is detected during page creation.
All HTTP requests are routed through .htaccess to .system/app.php. The router maps:
/welcome → Pages/public.welcome/code.php (PageWelcome class)/dashboard → Pages/private.dashboard/code.php (PageDashboard class)/assets/* → .assets/* (static files)App.call(url, method, data, callback) in JS → Apis/ PHP filesThe PHP layer is the server-side runtime for every PHPShift project. It is driven by a framework engine inside .system/ that handles routing, access control, database ORM, API dispatch and cron execution. Generated code lives on top of this engine.
Every page has a corresponding PHP class in its code.php file. Classes follow a strict naming convention:
edititem → Class name: PageEditItemdashboard → Class name: PageDashboardThe class is a plain PHP class with public methods. Each public method is directly callable from the front-end via JavaScript's App.call():
class PageDashboard
{
public function getStats($post = [], $get = [], $files = [])
{
$stats = DB::query("SELECT COUNT(*) as total FROM tasks WHERE user = :uid", [
':uid' => App::env('CURRENT_USER_ID')
]);
return App::done('Stats loaded', ['stats' => $stats]);
}
private function validateAccess($userId)
{
// Private helper - not callable from front-end
}
}
Method arguments are always ($post = [], $get = [], $files = []) - populated automatically by the framework from the incoming HTTP request.
API files live in Apis/ and follow a similar pattern, but declare their category and allowed HTTP method at the top:
<?php category('public', 'GET');
class ApiGetItems
{
public function init($json = [], $post = [], $get = [])
{
// API entry point - always named 'init'
return App::done('Items retrieved', ['items' => []]);
}
}
Available API categories:
- public - No authentication required
- callback - Webhook/callback endpoint
- systemApi - Internal system access
- userApi - Authenticated user access
- userWebhook - Authenticated user webhook
Cron files in Crons/ are PHP scripts executed by the PHPShift cron thread once per minute (or per schedule defined inside the file):
<?php
// Run at midnight
if (date('H:i') !== '00:00') exit;
// Cleanup expired sessions
DB::delete('sessions', 'expires_at < NOW()');
PHPShift manages cron execution through a Python background thread that calls php cron -auto every minute. You can start and stop cron processing from inside the skill menu under More > StartCrons and More > StopCrons.
App is a global PHP static utility class provided by the framework engine. Generated code uses it for all framework interactions:
| Method | Purpose |
|--------|---------|
| App::env($key) | Read .env variable |
| App::done($msg, $data) | Return {"message": ..., "response": ..., "error": false} |
| App::failed($msg) | Return {"message": ..., "error": true} |
| App::info($msg, $data) | Return informational response |
| App::error($msg) | Log a developer error |
| App::lng($key, $text, $vars) | Return translated string |
All API responses follow the same JSON envelope so the front-end App.call() handler always knows what to expect.
Generated PHP code can require Composer packages. When PHPShift's AI detects a needed package (e.g., phpmailer/phpmailer), it includes it in config.json under composer-plugins. PHPShift then prompts you to confirm before running:
composer require phpmailer/phpmailer
The vendor/ directory and composer.json are both patch-tracked, so plugin installation can be rolled back if needed.
All sensitive configuration is stored in .env and read via App::env(). Generated code uses this pattern to avoid hardcoding credentials:
$apiKey = App::env('STRIPE_API_KEY');
When the AI detects an App::env() call referencing a variable that doesn't exist in .env yet, PHPShift adds it automatically with an empty value and opens the file in VS Code for you to fill in.
The AI engine in PHPShift is the aisi Python module - a purpose-built skill runner that assembles context-rich prompts and communicates with AI models to generate structured file output.
Each skill is a folder containing two files:
prompt.md - The AI prompt template with [[placeholder]] injection pointsskill.py - The Python skill runner class that controls execution flowSome skills also have a readme.md describing the skill's purpose for developers.
Prompt files use double-bracket placeholders that are replaced with live project data before the prompt is sent to the AI:
[[MESSAGE]] → The developer's natural language task description
[[databaseSchema]] → Current MySQL schema (tables, columns, types)
[[databaseVersion]] → MySQL server version string
[[files]] → List of all Pages/, Apis/, Crons/ file paths
[[phpMethods]] → All PHP public static methods tagged with (AI-USE)
[[jsMethods]] → All JavaScript App object methods tagged with (AI-USE)
[[styling]] → CSS from a developer-selected existing page
[[pages]] → readme.md content from all existing pages
[[codeBase]] → Full file contents of a target page
[[logContent]] → Content of a selected log file
[[frameworkStructure]] → .system/ PHP framework directory listing
[[frameworkCodebase]] → Core framework PHP file contents
[[frameworkSolvedIssues]] → Resolved issue notes from solved.md
These placeholders are populated by the Collectors class at runtime, ensuring the AI always has accurate, current information.
Collectors is a Python class in .system/collectors.py where each method corresponds to a named placeholder. The aisi module calls the appropriate collector method based on which placeholders appear in the skill's prompt.
For example, [[phpMethods]] triggers Collectors.phpMethods(), which:
.php file in .system//** (AI-USE) doc commentsThis ensures the AI knows exactly which methods exist and how to use them - without you having to document anything extra.
The AI responds with named file blocks in markdown format:
[readme.md]
```md
## Goal
Create a user registration page...
```
[page.html]
```html
<div class="register-form">
...
</div>
```
[code.php]
```php
class PageRegister {
public function register($post = [], ...) {
...
}
}
```
aisi parses these blocks, extracts each filename and content pair and writes them to disk. Binary or special files are handled accordingly.
PHPShift supports multiple AI providers through the AISI.models() selector. During phpshift new or More > ChangeAIModel, you pick a model from an interactive list. The model identifier and API key are stored in .env:
PHPSHIFT_AIMODEL="claude-3-5-sonnet-20241022"
PHPSHIFT_AIKEY="sk-ant-..."
For Google Vertex AI, the PHPSHIFT_AIKEY value is a relative path to a service account JSON file:
PHPSHIFT_AIMODEL="vertex/gemini-1.5-pro"
PHPSHIFT_AIKEY="credentials/service-account.json"
Developer selects skill + types task description
↓
AISI loads prompt.md for the selected skill
↓
AISI calls Collectors for each [[placeholder]] found
↓
Full prompt assembled with live project context
↓
Prompt sent to AI model (via configured API)
↓
AI returns structured file blocks
↓
AISI parses response, writes files to disk
↓
PHPShift shows result, prompts for confirmation
The main CLI menu is built dynamically by scanning the Create/, Edit/, Delete/, Read/, Render/, More/ and PHPShift/ directories for skill.py files. The menu structure mirrors the directory tree and options are filtered based on project state (e.g., "Edit API" is hidden if no APIs exist yet).
PHPShift manages MySQL databases through two layers: a Python-side DB module for CLI operations (schema inspection, patch rollback, session management) and a PHP-side ORM provided by the .system/ framework engine for runtime query execution.
The DB class (modules/database.py) is used by PHPShift's CLI during the development session - not by the running PHP project. Its responsibilities include:
.env credentialsDB.schema() used by the [[databaseSchema]] collector, giving the AI an accurate picture of all existing tablesDB.reserve() snapshots pending changes; DB.rollback() reverts them if the developer rejects AI outputDB.new(name) runs setup.sql to initialize the database and create framework system tablesDB.insert(), DB.update(), DB.delete(), DB.query() are available for CLI-side data operationsEvery time a skill is about to generate code that may run SQL, PHPShift calls DB.reserve() to begin a tracked transaction. If the developer accepts the changes, DB.clear() finalizes them. If they reject, DB.rollback() reverts all database mutations made during that skill run.
At runtime, the PHP framework's .system/ engine provides a database abstraction layer accessed through static methods. Generated page and API code uses these exclusively - never raw PDO or mysqli.
Common patterns in generated PHP:
// Fetch records
$items = DB::select('SELECT * FROM tasks WHERE user = :uid', [':uid' => $userId]);
// Insert a record
$id = DB::insert('tasks', ['title' => $title, 'user' => $userId, 'created' => date('Y-m-d H:i:s')]);
// Update a record
DB::update('tasks', ['title' => $newTitle], 'id = :id', [':id' => $taskId]);
// Delete a record
DB::delete('tasks', 'id = :id', [':id' => $taskId]);
These methods are among the [[phpMethods]] collected by Collectors.phpMethods() and made available to the AI when generating code, ensuring correct usage every time.
When a new project database is initialized, PHPShift runs setup.sql which creates these framework-level tables:
| Table | Purpose |
|-------|---------|
| user_files | Tracks user-uploaded file references (path, mime, token) |
| user_keys | Stores named key-value pairs per user (sessions, tokens, settings) |
| api | Manages registered API access tokens with expiry |
These tables are used by the framework engine itself. Your application tables are created by the db.sql files generated alongside your pages.
When PHPShift's AI generates SQL, it follows strict rules enforced in every prompt:
CREATE DATABASE statement (PHPShift handles database creation separately)DEFAULT NULL or DEFAULT CURRENT_TIMESTAMP - never a fixed value[[databaseVersion]] to prevent syntax incompatibilitiesThe Read > Database skill lets you query your database using natural language during a development session. You describe what data you want and the AI generates a statement.sql and params.json which PHPShift then executes and displays inline. This is useful for debugging, inspecting data, or verifying that generated inserts worked correctly.
Pages are the primary building block of a PHPShift project. Each page is a self-contained, full-stack unit: a folder containing HTML structure, CSS styles, JavaScript interactions, PHP business logic and a documentation file.
A page in PHPShift corresponds to a single URL route in your application. When a visitor navigates to /dashboard, the framework routes the request to the Pages/private.dashboard/ folder and serves its HTML through code.php.
Pages are not templates or partials - each one is a complete, independently functional unit. However, they share:
.assets/ (base CSS, global JS, jQuery, jQook).system/ (App helper, DB ORM, access control)Space/seo.htmlSpace/lng.*.jsonCreate > Page or Render > PagePages/{group}.{name}/db.sql) is optionally applied.access if the group is new/{name} (not /{group}/{name})Note: Pages are accessed by their name only in URLs - the group is invisible to visitors. /edititem not /public/edititem.
| File | Required | Purpose |
|------|----------|---------|
| readme.md | Yes | AI-maintained documentation of page goal, functionality, usage |
| code.php | Yes | PHP class with business logic methods (even if just an empty class) |
| page.html | If needed | HTML structure (no <html>, <head>, or asset links) |
| style.css | If needed | Scoped responsive styles for this page |
| script.js | If needed | jQuery/jQook interaction logic |
| db.sql | Temporary | MySQL migration - deleted after being applied |
| config.json | Temp | Metadata used by PHPShift during generation - not stored permanently |
The {group} prefix in the folder name determines which users can access the page. Groups are defined as static methods in the .access PHP class:
class Access {
public static function public() { return true; }
public static function private() { return App::isLoggedIn(); }
public static function admin() { return App::isAdmin(); }
}
When you create a page with a new group name, PHPShift detects that the group method doesn't exist and automatically runs the Render > Group skill to generate it.
Every page's readme.md is AI-generated and AI-readable. It serves as:
[[pages]] collector reads all readme.md files to give the AI project-wide page awarenessREADME.md exists, the [[aboutProject]] collector assembles content from page readme filesKeep readme.md accurate - it's the AI's memory of what each page does.
Understanding the internal structure of a PHPShift page helps you navigate generated code, make manual adjustments and know how the PHP and JavaScript layers communicate.
page.html)The HTML file contains only the body content of the page - no <!DOCTYPE html>, <html>, <head>, or <body> tags. The framework injects those automatically along with:
style.cssscript.js.assets/style.css.assets/script.jsSpace/seo.htmlImages reference /assets/default.png, videos /assets/default.mp4, audio /assets/default.mp3.
Font Awesome free icons are available in HTML (the framework includes them).
Example HTML structure:
<div class="hero-section">
<h1 translate="hero_title">Welcome to MyApp</h1>
<p translate="hero_subtitle">The fastest way to manage your tasks.</p>
<button id="cta-btn">Get Started</button>
</div>
The translate attributes are added by the Create > Translation skill to mark strings for multilingual support.
style.css)Styles are scoped to the page and generated to match existing pages' design language. When creating a new page, PHPShift asks which existing page to use as a styling example - the AI receives that page's CSS and replicates its design system.
Generated CSS is fully responsive and avoids inline styles.
script.js)Page JavaScript uses jQuery and the global App JavaScript object. The key communication method is App.call():
App.call('dashboard', 'getStats', {}, (echo) => {
if (echo.error) {
App.alert(echo.message, 'error');
return;
}
$('#total-tasks').text(echo.response.stats[0].total);
});
App.call(url, method, data, callback):
- url - page name (maps to Pages/public.{url}/)
- method - public method name in the page's PHP class
- data - object sent as POST body
- callback - receives echo object: {message, response, error}
Translation strings in JS use App.lng():
const label = App.lng('save_button', 'Save Changes');
code.php)The PHP class handles all server-side logic. It is named with the page name in PascalCase with the Page prefix:
class PageDashboard
{
// Called via App.call('dashboard', 'getStats', {...}, callback)
public function getStats($post = [], $get = [], $files = [])
{
// Validate inputs, query database, return response
return App::done('Stats loaded', ['count' => 42]);
}
private function validateUser($userId)
{
// Private helpers are not callable from JS
}
}
Rules enforced by the AI:
- All JS-callable methods are public
- Arguments are always ($post = [], $get = [], $files = [])
- Input validation is always included
- Environment variables are read via App::env('KEY_NAME')
- Redirects are never done in PHP - always via JavaScript
User action in browser
↓
script.js: App.call('pagename', 'methodName', data, callback)
↓
Framework routes POST to Pages/group.pagename/code.php → PagePagename::methodName()
↓
PHP processes → returns App::done(message, response)
↓
callback(echo) in script.js receives { message, response, error }
↓
JS updates the DOM
No separate REST endpoints are needed for page-level interactions - the App.call() system handles PHP method dispatch transparently.
Access groups are PHPShift's authorization system for pages. Every page belongs to a group and the group determines which users can access it.
The group is the first part of a page's folder name: Pages/{group}.{name}/. The framework checks the corresponding static method in the .access PHP class before serving any page:
class Access
{
public static function public()
{
// Everyone can access 'public' pages
return true;
}
public static function private()
{
// Only logged-in users can access 'private' pages
return App::isLoggedIn();
}
public static function admin()
{
// Only admin users
return App::isAdmin() && App::isLoggedIn();
}
}
If the access method returns false, the framework redirects or returns a 403 response.
| Group | Typical Use |
|-------|------------|
| public | Pages accessible to all visitors (landing, login, signup) |
| private | Pages for authenticated users only (dashboard, profile) |
| admin | Pages for administrators only |
| callback | Webhook receiver endpoints (no standard auth) |
You can define any group name - the AI respects whatever name you use.
When the AI creates a page with a group that doesn't have an existing method in .access, PHPShift automatically runs the Render > Group skill. This skill:
.accessFor example, creating a page with group premium triggers generation of:
public static function premium()
{
// Premium means only subscribed users with active premium plan can access
return App::isLoggedIn() && App::env('USER_PLAN') === 'premium';
}
The description used to generate this method comes from the group-description field in config.json that the AI populates when creating the page:
{
"page-group": "premium",
"group-description": "premium - means that only subscribed users with an active premium plan can access the page"
}
Within page PHP code, you don't need to manually check group access - the framework handles it before your class is even instantiated. However, if you need to verify access inside a method (e.g., for an App.call() request), use the App helper:
public function getPrivateData($post = [], $get = [], $files = [])
{
if (!App::isLoggedIn()) {
return App::failed('Unauthorized');
}
// ...
}
The Edit > Access skill lets you modify the .access file using AI. Describe what you need ("add a moderator group that requires the user to have role='mod' in the database") and PHPShift updates the class while preserving existing group methods.
PHPShift supports standalone REST API files for endpoints that operate independently of pages - webhooks, mobile app backends, external integrations and internal system services.
Use App.call() page methods when:
- The front-end JavaScript on the same page is the caller
- The interaction is tightly coupled to a specific page
- You're building CRUD operations for that page's data
Use API files when: - The caller is external (mobile app, third-party service, webhook) - Multiple pages or services need the same endpoint - You need versioned API routes (v1, v2) - You're building a public API with token authentication
API files live in Apis/ and follow strict naming:
Apis/
├── v1.getItems.php
├── v1.createItem.php
├── v2.getItems.php
└── v1.processWebhook.php
v1, v2, etc.{version}.{camelCaseName}.phpThe framework routes API calls automatically. An API file v1.getItems.php is accessible at:
GET /api/v1/getItems
The router parses the version and name from the URL and dispatches to the correct file.
Each API file declares its category (access control) and allowed HTTP method on the first line:
<?php category('public', 'GET');
| Category | Who Can Call |
|----------|-------------|
| public | Anyone without authentication |
| callback | Webhook/callback source with verification |
| systemApi | Internal system (server-to-server) |
| userApi | Authenticated users with valid API token |
| userWebhook | Authenticated user webhook with token |
All API responses use the same JSON envelope as page methods:
{
"message": "Items retrieved successfully",
"response": [...],
"error": false
}
Use App::done(), App::failed(), or App::info() to produce these - never echo json_encode() directly.
| Action | Skill |
|--------|-------|
| Create a new API | Create > API |
| Edit an existing API | Edit > API |
| Delete an API | Delete > API |
PHPShift's skill menu dynamically shows or hides Edit/Delete API options depending on whether any API files currently exist in Apis/.
Every PHPShift API file follows a strict structure enforced by AI generation rules and the framework's dispatch engine.
// Specify API category and allowed HTTP method
<?php category('userApi', 'POST');
/**
* Required parameters:
* - $json['item_id'] (int) - ID of item to update
* - $json['title'] (string) - New item title
*/
// Class name: 'Api' prefix + PascalCase name
class ApiUpdateItem
{
// Entry point - always 'init', always these three arguments
public function init($json = [], $post = [], $get = [])
{
$itemId = (int)($json['item_id'] ?? 0);
$title = trim($json['title'] ?? '');
if (!$itemId || !$title) {
return App::failed('Invalid parameters');
}
$updated = DB::update('items', ['title' => $title], 'id = :id', [':id' => $itemId]);
if (!$updated) {
return App::failed('Update failed');
}
return App::done('Item updated', ['item_id' => $itemId]);
}
private function validateOwnership($itemId, $userId)
{
$item = DB::query('SELECT user FROM items WHERE id = :id', [':id' => $itemId]);
return !empty($item) && $item[0]['user'] === $userId;
}
}
| Element | Rule | Example |
|---------|------|---------|
| File name | {version}.{camelCase}.php | v1.updateItem.php |
| Class name | Api + PascalCase of name | ApiUpdateItem |
| Entry method | Always init | public function init(...) |
| Helper methods | private | private function validateOwnership(...) |
The init method receives three arguments:
$json - JSON body (for POST/PUT requests with Content-Type: application/json)$post - Form POST fields$get - URL query string parametersAlways validate and sanitize all inputs before using them.
| Method | Use When |
|--------|---------|
| App::done($msg, $data) | Success with data |
| App::failed($msg) | Business logic failure (not a server error) |
| App::info($msg, $data) | Informational response |
| App::error($msg) | Log a developer-facing error (does not return to caller) |
From a mobile app or external service, call the API endpoint directly:
POST https://myapp.com/api/v1/updateItem
Content-Type: application/json
X-System: {api_token}
{
"item_id": 42,
"title": "New Title"
}
The X-System header is used for API token authentication in userApi and systemApi categories. The framework's token validation is handled automatically before your init() method is called.
For API endpoints that your own front-end JavaScript calls, you can use App.call() pointing to the API path:
App.call('api/v1/updateItem', 'init', {item_id: 42, title: 'New Title'}, (echo) => {
if (!echo.error) console.log('Updated!');
});
However, for page-level interactions, generating a page method (via Create > Page or Edit > Page) is preferred over separate API files.
PHPShift includes a built-in cron job management system that runs scheduled PHP scripts in the background during your development session - and on your production server via a standard system cron.
Cron jobs are background tasks that run on a schedule, independent of user requests. Common uses in PHPShift projects:
Cron files live in Crons/:
Crons/
├── sendDigest.php
├── cleanExpired.php
└── syncPrices.php
During a development session (phpshift start), if cron files exist, PHPShift asks "Want to start cron jobs?" A Python background thread is started that fires:
php cron -auto
once every minute. The cron entry point file (at project root) reads all files in Crons/, determines which ones should run based on their internal time checks and executes them.
You can manually control cron execution from the skill menu:
More > StartCrons - Start the background cron threadMore > StopCrons - Stop the background cron threadOn your production server, set up a system cron to call the same entry point:
* * * * * php /var/www/myapp/cron -auto
The cron files themselves contain internal schedule logic (e.g., only run at midnight), so running cron -auto every minute is safe - each job self-determines whether it's time to execute.
| Action | Skill |
|--------|-------|
| Create a new cron job | Create > CronJob |
| Edit an existing cron job | Edit > CronJob |
| Delete a cron job | Delete > CronJob |
The skill menu shows Edit/Delete Cron options only when cron files exist in Crons/.
PHPShift cron files are PHP scripts that run autonomously. They have direct access to the full framework - database ORM, App helpers, environment variables - just like page PHP classes.
<?php
// Only run once per day at midnight
if (date('H:i') !== '00:00') exit;
// Clean up expired API tokens
$deleted = DB::delete('api', 'expires < :now AND expires > 0', [':now' => time()]);
if ($deleted) {
App::error("Cleaned " . count($deleted) . " expired API tokens");
}
<?php
// Run every hour at :00
if (date('i') !== '00') exit;
// Get users with pending notifications
$users = DB::query(
'SELECT u.email, COUNT(n.id) as count FROM notifications n
JOIN users u ON u.id = n.user
WHERE n.sent = 0 AND n.scheduled <= NOW()
GROUP BY u.id',
[]
);
foreach ($users as $user) {
// Send notification email
$mailer = new PHPMailer\PHPMailer\PHPMailer();
// ... configure and send
// Mark as sent
DB::update('notifications', ['sent' => 1], 'user = :uid AND sent = 0', [
':uid' => $user['id']
]);
}
| Element | Rule | Example |
|---------|------|---------|
| File name | camelCase .php | sendDigest.php |
| No class required | Plain script | - |
| Schedule logic | Internal if check | if (date('H:i') !== '00:00') exit; |
Unlike pages and APIs, cron files are not classes. They are plain PHP scripts executed directly by the cron runner.
Cron files have access to everything the framework provides:
// Read environment variables
$apiKey = App::env('EXTERNAL_API_KEY');
// Database queries
$records = DB::query('SELECT * FROM items WHERE ...', [...]);
DB::insert('logs', ['action' => 'cron_ran', 'time' => date('Y-m-d H:i:s')]);
// Translations (if needed for notification text)
$subject = App::lng('digest_subject', 'Your daily digest');
// Error logging
App::error('Something went wrong in cron: ' . $errorMessage);
The Create > CronJob skill generates a fully functional cron file. Describe what the job should do and when:
Every day at 2 AM, find all users who have not logged in for 30 days
and send them a re-engagement email using PHPMailer
PHPShift provides the AI with your current database schema and available PHP methods, so generated cron code correctly references your actual table names and columns.
After generation, if the cron needs a Composer plugin (like PHPMailer), PHPShift installs it immediately.
Cron jobs can write to log files in Space/. Use the Read > Log skill to have the AI analyze a log file and summarize errors or anomalies.
The Create > Project skill generates a complete structural plan for your web project: a README.md, a database schema (db.sql) and individual markdown description files for every page, API and cron job the AI determines is necessary.
Given a plain-language project description, the AI produces:
README.md - Project overview: goal, pages, APIs, cron jobs and usage summarydb.sql - Full MySQL schema with example records, ready to executepage/{group}/{name}.md - Description file for each pageapi/{version}.{name}.md - Description file for each APIcron/{name}.md - Description file for each cron jobconfig.json - Specifies the landing pageThese markdown description files become blueprints - feed them one by one into Create > Page, Create > API and Create > CronJob to generate actual code.
Use Create > Project at the very start of a project to establish structure. It answers: "What pages, APIs and cron jobs does this project need?"
For adding major new sections to an existing project, use Edit > Project.
A subscription recipe platform where users sign up, create and share recipes,
comment on others' recipes and access premium recipes with a paid plan.
Include admin moderation tools.
The AI produces a complete plan covering auth pages, recipe CRUD, commenting, subscription handling, admin tools and background cron jobs.
This skill generates planning documents only - no PHP, HTML, CSS, or JavaScript is written and no database changes are applied. Actual code is generated per-unit via Create > Page etc.
Review the generated README.md and description files. Edit them to better reflect your vision before using them as code-generation input. The richer the descriptions, the higher quality the generated code will be.
The Create > Page skill generates a complete, working full-stack page: HTML structure, CSS styles, JavaScript interactions, PHP business logic, a database migration and documentation.
Before generating, PHPShift collects:
.system/ with AI-USE annotations)App object methodsThis ensures new pages are consistent with your existing codebase.
| File | Always? | Purpose |
|------|---------|---------|
| readme.md | Yes | Page goal, functionality and usage docs |
| code.php | Yes | PHP class with business logic (even if minimal) |
| page.html | If needed | HTML body structure |
| style.css | If needed | Page-specific responsive styles |
| script.js | If needed | jQuery/jQook front-end logic |
| db.sql | If needed | MySQL migration to apply immediately |
| config.json | Yes | Page metadata (name, group, composer deps) |
A private dashboard page showing the logged-in user's task count,
recent activity feed and a quick-add task form
PHPShift sends the AI your database schema, existing PHP methods and a CSS example. The AI generates a complete dashboard page that uses your actual table names and PHP method signatures.
When creating a page, PHPShift asks "Select styling example" - choose an existing page whose CSS design you want the new page to match. If no pages exist yet, you can skip this.
If your page uses a new access group (e.g., premium) that doesn't exist yet in .access, PHPShift automatically runs Render > Group to generate the access method.
If the AI generates a db.sql file, PHPShift opens it in VS Code and asks for confirmation before applying it to the database. If you decline the changes at the review step, the database changes are rolled back automatically.
The Create > API skill generates one or more PHP REST API files in Apis/. Use it when you need versioned endpoints callable from external clients (mobile apps, third-party services, webhooks).
.system/The AI produces one or more PHP files in the format v{version}.{camelCaseName}.php, each containing a properly structured API class.
Create a v1 public API endpoint that returns a paginated list of public recipes,
supporting optional category filter and keyword search via GET parameters
Output: Apis/v1.getRecipes.php
<?php category('public', 'GET');
class ApiGetRecipes
{
public function init($json = [], $post = [], $get = [])
{
$page = max(1, (int)($get['page'] ?? 1));
$category = trim($get['category'] ?? '');
$keyword = trim($get['keyword'] ?? '');
$limit = 20;
$offset = ($page - 1) * $limit;
$where = 'r.public = 1';
$params = [];
if ($category) {
$where .= ' AND r.category = :cat';
$params[':cat'] = $category;
}
if ($keyword) {
$where .= ' AND r.title LIKE :kw';
$params[':kw'] = '%' . $keyword . '%';
}
$params[':limit'] = $limit;
$params[':offset'] = $offset;
$recipes = DB::query(
"SELECT r.id, r.title, r.category, r.created
FROM recipes r WHERE {$where}
ORDER BY r.created DESC LIMIT :limit OFFSET :offset",
$params
);
return App::done('Recipes retrieved', ['recipes' => $recipes, 'page' => $page]);
}
}
Always specify the version in your task description. The AI uses it as the file version prefix. When creating a v2 update to an existing endpoint, the original v1 file is untouched.
If the generated API requires a Composer package, PHPShift detects it from config.json and prompts you to install it before confirming the skill run.
The Create > CronJob skill generates a PHP cron job script in Crons/. Cron jobs run on a schedule and handle background tasks independent of user requests.
.system/A single PHP file in Crons/ named in camelCase (e.g., cleanExpiredSessions.php). The file contains:
Every day at 3 AM, delete sessions from the sessions table that expired
more than 7 days ago and log the count of deleted records
Output: Crons/cleanExpiredSessions.php
<?php
if (date('H:i') !== '03:00') exit;
$cutoff = date('Y-m-d H:i:s', strtotime('-7 days'));
$deleted = DB::delete('sessions', 'expires_at < :cutoff', [':cutoff' => $cutoff]);
App::error("Cleaned expired sessions. Count: " . ($deleted ? 'success' : '0'));
Describe your schedule in natural language - the AI translates it to PHP time-check logic:
| Description | Generated guard |
|-------------|----------------|
| Every minute | (no guard - runs every invocation) |
| Every hour | if (date('i') !== '00') exit; |
| Every day at midnight | if (date('H:i') !== '00:00') exit; |
| Every Monday at 8 AM | if (date('D H:i') !== 'Mon 08:00') exit; |
| First day of month | if (date('d H:i') !== '01 00:00') exit; |
If the cron job needs a package (e.g., phpmailer/phpmailer for email), PHPShift detects it from config.json and installs it before confirming.
The cron file is immediately executable. If the PHPShift cron thread is running (More > StartCrons), it will be picked up within the next minute cycle. You can also test it manually:
php cron sendDigest
The Create > Translation skill adds full multi-language support to an existing page in one operation. It modifies HTML, CSS, JavaScript and PHP files to integrate translation tags, then generates the translation JSON file for the requested language.
Given a page and a target language, the AI:
Updates page.html - Adds a <translation languages="en,{lang}"> tag to the page (or appends the new language to an existing one). Adds translate="{key}" attributes to all hardcoded text elements.
Updates style.css - Adds translation and translation img CSS rules for the language switcher UI component.
Updates script.js - Wraps hardcoded strings in App.lng(key, text) calls.
Updates code.php - Wraps PHP hardcoded strings in App::lng(key, text) calls.
Creates Space/lng.{lang}.json - The translation file containing all key-value pairs for the requested language.
Add Georgian (ka) translation to the dashboard page
Keys are unique, descriptive and lowercase with underscores:
<h1 translate="dashboard_title">My Dashboard</h1>
<button translate="add_task_btn">Add Task</button>
// Space/lng.ka.json
{
"dashboard_title": "ჩემი სამუშაო მაგიდა",
"add_task_btn": "დავალების დამატება"
}
For strings with variables, the {variable} syntax is used:
App.lng('welcome_msg', 'Hello, {name}!', {name: username});
{ "welcome_msg": "გამარჯობა, {name}!" }
Run Create > Translation again with a different language code. The skill detects the existing <translation> tag and appends the new language abbreviation to its languages attribute.
The Create > SEO skill generates Space/seo.html - the project-level SEO metadata file included in every page's <head>.
A complete HTML snippet containing:
og:title, og:description, og:image, og:url)SEO for a recipe sharing platform called "RecipeHub" targeting home cooks,
using the share.png image and focusing on keywords around easy recipes and cooking tips
The SEO file is shared across all pages. Page-specific SEO overrides should be set dynamically in individual code.php files using framework SEO helpers.
The Create > README skill generates or regenerates README.md at the project root. It reads your existing pages' readme.md files and the project .md briefing to produce a comprehensive, professional readme.
Edit > ProjectThe Create > LICENSE skill installs a license file at the project root. PHPShift includes a library of common open source licenses and lets you select one interactively.
The selected license is populated with your project author name (from PROJECT_AUTHOR in .env) and the current year.
This skill is only shown in the menu if no LICENSE file exists yet. Once a license is installed, use Edit > LICENSE to change it or Delete > LICENSE to remove it.
The Create > SEO skill generates Space/seo.html - the project-level SEO metadata file included in every page's <head>.
A complete HTML snippet containing:
og:title, og:description, og:image, og:url)<meta name="description"> and keyword tagsSEO for a recipe platform called "RecipeHub" targeting home cooks.
Keywords: easy recipes, cooking tips, meal planning.
Use share.png as the social image.
Space/seo.html is automatically included in every page's <head> by the framework. There is no need to reference it manually.
This file sets global defaults for all pages. For page-specific SEO overrides (different title, different description per page), use PHP App helper methods inside individual code.php files to dynamically override the meta tags for that route.
Preview the SEO output by inspecting <head> in your browser on any page. The Space/seo.html file is editable manually or via the Edit > SEO skill.
The framework auto-generates Space/sitemap.xml with the current date when pages are added. The .htaccess serves it at /sitemap.xml automatically.
The Create > README skill generates a professional README.md at the project root. It reads your project's existing page documentation files and the .md briefing to produce a comprehensive developer-facing readme.
Edit > ProjectThe skill reads:
- Your .md project briefing file
- readme.md files from all existing pages
- Project metadata from .env (name, author, production domain)
This skill is only shown if no README.md exists yet. Once created, use Edit > README to update it or Delete > README to remove it.
The Create > LICENSE skill installs a license file in your project root from PHPShift's built-in license library.
PHPShift presents an interactive selection menu. After you choose a license:
licenses/ directory{{year}} is replaced with the current year{{author}} is replaced with PROJECT_AUTHOR from your .env{project}/LICENSESelecting MIT License with PROJECT_AUTHOR="Jane Doe" and current year 2025 produces:
MIT License
Copyright (c) 2025 Jane Doe
Permission is hereby granted, free of charge, to any person obtaining a copy...
This skill only appears in the menu when no LICENSE file exists. After creation, the menu switches to Edit > LICENSE and Delete > LICENSE.
The Edit > Project skill updates an existing project plan when you need to add, change, or remove features across multiple pages. It works like Create > Project but with full awareness of what already exists.
readme.md files)strategy.md - A strategic overview of what needs to change and whydb.sql (if schema changes are needed) - Ready-to-execute migration SQL{group}.{page}.md description files for pages that need updatesAdd a social features layer: users should be able to follow each other,
like recipes and see a social feed of activity from people they follow
The AI analyzes your existing pages and generates targeted update descriptions only for pages that need to change, plus the new pages/APIs required.
Use the generated .md description files as input for Edit > Page on each affected page to generate the actual code changes.
The Edit > Page skill modifies an existing page. It receives the full current codebase of the target page and regenerates only the files that need to change.
page.html, style.css, script.js, code.php)App methodsEdit > Page from the menuPHPShift sets the VAR.target variable to the selected page folder so the [[codeBase]] collector loads the right files.
Add a search and filter bar to the top of the page that filters the task list
in real-time by title and status without page reload
The AI receives your existing page code and generates updated versions of only the files that need modification - leaving unchanged files intact.
The Edit > Page loop supports multi-turn refinement. If you select Redo, PHPShift asks you to adjust your description and retries. You can specify what was wrong:
The filter works but the results don't clear when I delete my search text.
Also the status dropdown styling doesn't match the rest of the page.
db.sql if tables need changes)config.json group settings)The Edit > API skill modifies an existing API file. Select the API file to edit and describe what needs to change - the AI rewrites it with full awareness of your current database schema and available PHP methods.
Add pagination support to v1.getRecipes.php - accept 'page' and 'per_page'
GET parameters, return total count alongside the results
This option is only shown when at least one API file exists in Apis/.
The Edit > CronJob skill modifies an existing cron job file. Select the cron to edit and describe the required changes - the AI updates the schedule logic, database queries, or notification logic as needed.
Change the digest cron to run at 9 AM instead of midnight,
and add a check to skip users who have unsubscribed from email notifications
This option is only shown when at least one cron file exists in Crons/.
The Edit > Translation skill updates an existing translation - either correcting translation values in a lng.*.json file, adding new keys, or updating translated strings across multiple pages.
Update the Georgian translation file to translate the new task status labels
we added to the dashboard page last session
Space/lng.{lang}.json filestranslate attributes on pages that have new untranslated textlng() calls for new hardcoded stringsThe Edit > SEO skill updates Space/seo.html. Use it when your project name, description, keywords, or social image changes - or when you want to add JSON-LD structured data.
Update the SEO to reflect our new product name "RecipeVault" and
add recipe schema JSON-LD structured data
Only shown when Space/seo.html has been initialized (via Create > SEO).
The Edit > README skill regenerates or updates the project's README.md. Use it after adding major features, changing the project scope, or before a public release to ensure the readme reflects the current state of the project.
Update the readme to include the new API endpoints we added and
expand the setup section with database migration instructions
The Edit > Access skill modifies the .access PHP class - the file that defines page access group authorization methods.
The AI receives the current .access content and the edit request, then generates an updated version of the file that adds, modifies, or removes group methods.
Add a 'moderator' group that requires the user to have role='mod' in the users table
Update the 'premium' group check to also verify the subscription hasn't expired
Every static method in .access corresponds to an access group used by pages in Pages/{group}.{name}/. Removing a group method will break all pages that use that group. PHPShift does not automatically check for this - review your page folders before deleting groups.
Removes a page and all its associated files from the project.
The entire Pages/{group}.{name}/ folder including page.html, style.css, script.js, code.php and readme.md.
File deletions are tracked by the patch system. If you decide to undo before ending the session, roll back via the Redo option.
Deleting a page does not drop associated database tables. If you want to clean up tables, use Read > Database to run the appropriate DROP queries.
Only shown when custom pages exist (the default public.phpshift page is excluded).
Removes an API file from Apis/.
.php file is deletedThis does not remove associated database tables. Use Read > Database for cleanup SQL if needed.
Only shown when at least one API file exists in Apis/.
Removes a cron job file from Crons/.
.php file is deletedIf the cron thread is running, the removed job will simply not be found on the next execution cycle.
Only shown when at least one cron file exists in Crons/.
Removes a language translation JSON file from Space/.
The Space/lng.{lang}.json file for the selected language. Translation tag references in HTML pages are not automatically cleaned up - you may want to run Edit > Page on affected pages to remove the unused language from <translation languages="..."> tags.
Only shown when at least one lng.*.json file exists in Space/.
Removes a log file from Space/.
PHPShift lists all *.log files in Space/. You select which one to delete and confirm.
Log files can grow large. Clear them after reviewing issues (via Read > Log) or before production deployments.
Only shown when at least one .log file exists in Space/.
Removes the README.md file from the project root.
Create > README from scratchOnly shown when README.md exists. After deletion, the menu shows Create > README instead.
Removes the LICENSE file from the project root.
Create > LICENSE)Only shown when a LICENSE file exists. After deletion, the menu shows Create > LICENSE instead.
The Read > Database skill lets you query your project's MySQL database using plain English. The AI translates your request into a SQL statement and parameters, executes them and displays the results - no SQL knowledge required.
statement.sql + params.jsonShow me the 10 most recently created users with their email and created date
Count how many tasks are in each status category
Find all recipes that have more than 5 comments and were created this month
The skill produces two temporary files before execution:
statement.sql - The MySQL query (no markdown, no comments - pure SQL)params.json - Named parameters used in the query, or {} if noneThe AI is instructed to generate safe, parameterized queries only. No raw string interpolation, no destructive operations unless explicitly and unambiguously requested.
This skill executes immediately - it does not go through the patch/rollback system since it is read-oriented. Destructive queries (DELETE, DROP, TRUNCATE) are possible if explicitly requested, so use care when describing your task.
The Read > Git skill translates natural language into Git CLI commands and executes them in your project directory. It handles any Git operation - from inspecting history to managing branches to configuring remotes.
command.sh containing the appropriate Git commandShow me the last 10 commits with their dates and messages
Create a new branch called feature/user-auth and switch to it
Show all files changed in the last commit
Revert the most recent commit but keep the file changes staged
Show the diff between main and the current branch
A single command.sh file containing one Git command:
git log --oneline --format="%h %ad %s" --date=short -10
PHPShift's main workflow already handles git add, git commit and git push natively after every skill confirmation. Use Read > Git for everything else - branching, diffing, inspecting history, resetting, configuring remotes, etc.
The generated command runs in your project root (Help.cwd). Commands requiring credentials (push, pull, clone with auth) assume your Git config or SSH keys are already set up.
The Read > Log skill performs AI-powered analysis of your application's log files. It reads the log content and your description of what you're looking for, then produces a structured summary.md highlighting key issues, errors and anomalies.
*.log files in Space/summary.md reportFind all 500 errors from the last hour and tell me what caused them
Summarize all database errors in this log
Are there any repeated failed login attempts? Show me the patterns.
Give me a general health summary of this log file
A summary.md report with:
Application log files should be written to Space/ in your project. For example, a cron job might write to Space/digest.log. Your PHP code writes to these files using standard PHP file I/O or the App::error() logging utility.
Only shown when at least one .log file exists in Space/.
The Render > Page skill re-renders an existing page from scratch - regenerating all its files while preserving the page's identity (name, group) and staying consistent with your current database schema and PHP method surface.
| | Edit > Page | Render > Page | |--|------------|--------------| | Approach | Modifies specific parts of existing code | Regenerates all files from your description | | Starting point | Existing code is context | Existing readme.md is context | | Use when | Making targeted changes | Page code is outdated or needs a full rewrite | | Preserves | Most of the existing structure | Page name and group only |
Use Edit > Page for incremental improvements. Use Render > Page when a page's code has drifted too far from what you need and a clean regeneration makes more sense than patching.
readme.md as the base descriptionRe-render the dashboard page with an updated layout -
move the activity feed to the right sidebar and make the stats cards larger
readme.md (goal, functionality, usage)Review the regenerated files and confirm or roll back as usual. If you confirm, the old files are replaced by the fresh render.
The Render > Group skill generates a new static method in the .access PHP class for a page access group that doesn't exist yet.
This skill is usually called automatically by PHPShift - not manually. When Create > Page or Render > Page produces a config.json specifying a group that has no method in .access, PHPShift automatically runs Render > Group before finishing.
You can also run it manually if you want to pre-define an access group before creating pages that use it.
PHPShift passes the group name and its group-description from config.json to the AI. The AI generates a new public static function {groupName}() method and appends it to the .access class.
If config.json contains:
{
"page-group": "subscriber",
"group-description": "subscriber - means the user has an active paid subscription"
}
The generated method might be:
public static function subscriber()
{
if (!App::isLoggedIn()) return false;
$user = DB::query('SELECT plan FROM users WHERE id = :id', [':id' => App::userId()]);
return !empty($user) && $user[0]['plan'] === 'subscriber';
}
Run Render > Group manually when you want to:
Edit > Access referenceThe .access class is PHP. The static method names must match the group folder prefix exactly (case-insensitive matching may vary by server). Keep group names lowercase and simple.
All PHPShift project configuration lives in the .env file at the project root. This file is never exposed publicly (.htaccess denies all access to it) and is excluded from Git via .gitignore.
These are set during phpshift new and managed by PHPShift:
| Variable | Description | Example |
|----------|-------------|---------|
| PROJECT_NAME | Human-readable project name | My Recipe App |
| PROJECT_AUTHOR | Author name (used in LICENSE) | Jane Doe |
| PROJECT_LOCAL | Local development domain | myapp.local |
| PROJECT_PRODUCTION | Production domain | myapp.com |
| PROJECT_LANDING | Default landing page group/name | public/welcome |
| PROJECT_KEY | Auto-generated secure app key | (random token) |
| PHPSHIFT_AIMODEL | AI model identifier string | claude-3-5-sonnet-20241022 |
| PHPSHIFT_AIKEY | AI API key or key file path | sk-ant-... |
| DB_HOST | MySQL host | localhost |
| DB_NAME | MySQL database name | myapp_db |
| DB_USER | MySQL username | root |
| DB_PASS | MySQL password | secret |
| PRODUCTION | Production mode flag (1 or 0) | 0 |
| ALLOWED | Comma-separated allowed IPs (dev restriction) | 127.0.0.1 |
Below the ## Project comment section, PHPShift and the AI add custom variables as your project needs them. For example:
STRIPE_SECRET_KEY=""
SMTP_HOST=""
SMTP_PORT=""
SMTP_USER=""
SMTP_PASS=""
These are added automatically when generated PHP code contains App::env('VARIABLE_NAME') calls - PHPShift detects new variable names and appends them as empty entries, then opens the file in VS Code for you to fill in.
$key = App::env('STRIPE_SECRET_KEY');
$host = App::env('SMTP_HOST');
model = Help.getEnv('PHPSHIFT_AIMODEL')
Help.setEnv('PHPSHIFT_AIMODEL', 'new-model-name')
Set PRODUCTION="1" on your live server to enable production behaviors:
ALLOWED IP list is enforced (if set).env file is blocked by .htaccess with Require all denied.gitignore to prevent accidental commitsApp::env()PHPShift is model-agnostic - it supports any AI provider available through the aisi module. You choose your model during project creation and can switch at any time.
During phpshift new, you are presented with an interactive model selection list. The list is populated by AISI.models() from your installed aisi package.
To switch models during an active session, use More > ChangeAIModel. This updates PHPSHIFT_AIMODEL and PHPSHIFT_AIKEY in .env and reinitializes the AI connection immediately - no restart needed.
For most providers (Claude, OpenAI-compatible, etc.), configuration is straightforward:
PHPSHIFT_AIMODEL="claude-3-5-sonnet-20241022"
PHPSHIFT_AIKEY="sk-ant-api03-..."
The model string format depends on the provider. Refer to the aisi documentation for supported model identifiers.
Vertex AI uses a service account JSON key file instead of a plain API key:
PHPSHIFT_AIMODEL="vertex/gemini-1.5-pro"
PHPSHIFT_AIKEY="credentials/vertex-key.json"
The key file path is relative to your project root. PHPShift checks that this file exists before starting a session. If it's missing, the session won't start and you'll see "AI key file not found!"
Store your Vertex credentials file in your project directory but add it to .gitignore to prevent accidental commits.
PHPShift skills send large prompts with significant code context. For best results:
Create > Project and Create > Page send the most context and benefit most from capable modelsRead > Database and Read > Git are lightweight tasks suitable for faster/cheaper modelsMore > ChangeAIModel
PHPShift shows the model selection list, then asks for the new API key. Both values are updated in .env and the AISI instance is reinitialized in the current session.
PHPShift automates local development server configuration using XAMPP on Windows. When you run phpshift start, it sets up everything needed to access your project via a custom .local domain - no manual Apache or hosts file editing required.
PHPShift adds an entry to C:/Windows/System32/drivers/etc/hosts:
# phpshift_myapp-hosts
127.0.0.1 myapp.local
# phpshift_myapp-host
The comment markers (# phpshift_myapp-hosts and # phpshift_myapp-host) are used to identify and remove this entry cleanly when the project is stopped.
PHPShift adds a virtual host block to C:/xampp/apache/conf/extra/httpd-vhosts.conf:
# phpshift_myapp-vhost
<VirtualHost *:80>
DocumentRoot "C:/projects/myapp"
ServerName myapp.local
<Directory "C:/projects/myapp">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Again, the comment marker (# phpshift_myapp-vhost) allows PHPShift to locate and remove this block cleanly on stop.
If Apache and MySQL are not already running, PHPShift starts them by executing:
C:/xampp/xampp_start.exe
PHPShift checks whether a project's localhost is already configured by:
httpd.pid and mysql.pid exist (XAMPP running)httpd-vhosts.confhostsIf all three conditions are met, the start step is skipped (fast startup for already-configured projects).
When you exit PHPShift (phpshift stop or menu exit), it:
httpd-vhosts.confEach PHPShift project uses a unique marker tag (phpshift_{hint}) based on the local domain name. Multiple projects can coexist with separate virtual hosts. Stopping one project does not affect others.
PHPShift uses port 80 by default (standard HTTP). If another service occupies port 80, you'll need to adjust XAMPP's Apache port configuration manually in C:/xampp/apache/conf/httpd.conf before starting PHPShift.
XAMPP not found - Ensure XAMPP is installed at exactly C:/xampp. Custom paths are not currently supported without modifying the localhost.py module.
Permission denied on hosts file - PHPShift requires admin privileges to write to the Windows hosts file. Run your terminal as Administrator when starting PHPShift.
Virtual host not loading - Ensure Apache's httpd-vhosts.conf include is uncommented in httpd.conf. Look for Include conf/extra/httpd-vhosts.conf and make sure it's not commented out.
PHPShift uses MySQL (via XAMPP) for local development. All database connection details are stored in .env and managed through PHPShift's DB module.
DB_HOST="localhost"
DB_NAME="myapp_db"
DB_USER="root"
DB_PASS="your_password"
These are set during phpshift new and used by both the Python CLI layer (for schema inspection and patch management) and the PHP framework layer (for runtime query execution).
On first phpshift start, if the database doesn't exist, PHPShift creates it automatically by running setup.sql:
CREATE DATABASE IF NOT EXISTS `myapp_db` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE `myapp_db`;
-- Creates system tables: user_files, user_keys, api
If DB_NAME in .env is wrapped in brackets (e.g., [myapp_db]), PHPShift treats it as "not yet confirmed" and checks whether the database exists when starting. If it does, you're prompted:
This prevents accidental data loss when switching between project states.
PHPShift continuously provides the AI with your current schema via DB.schema() → [[databaseSchema]]. This means:
When a skill generates a db.sql file, PHPShift:
DB.submit() if confirmed.sql file after successful applicationIf the skill run is rejected (Redo or No), the database changes are rolled back via DB.rollback().
Use More > ExportDatabaseSchema to export your current database schema as a SQL file. Useful for:
For production, update .env on your server with the production database credentials. Set PRODUCTION="1" to enable production mode. PHPShift itself is not deployed to production - only the PHP framework and your project files are.
All PHPShift databases use utf8mb4 with utf8mb4_general_ci collation by default, ensuring full Unicode support including emoji.
PHPShift bakes security into the generated framework from day one. The .htaccess file, PHP framework engine and environment variable system all enforce protections that would take significant effort to implement manually in a typical PHP project.
Every PHPShift project is initialized with a security-hardened .htaccess that:
Options -Indexes.env, *.log, .system/storage/*X-Powered-By header is unset, ServerSignature OffThe .system/ PHP engine:
app.php)CONTENT_LENGTH limitThe AI generation prompts enforce:
App::redirect() in PHP (redirects handled by JS to avoid open redirect vulnerabilities)App::env() - never hardcoded credentialsDB::* callsprivate visibility for all non-callable PHP helper methods.env is for local development configThe .htaccess file generated by PHPShift is a security-first Apache configuration. Every project starts with this hardened baseline - no manual setup required.
Header set X-Frame-Options "SAMEORIGIN"
Header set X-Content-Type-Options "nosniff"
Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy "no-referrer"
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header set Access-Control-Allow-Headers "Content-Type, X-System"
| Header | Protection |
|--------|-----------|
| X-Frame-Options: SAMEORIGIN | Prevents clickjacking by blocking iframe embedding from other origins |
| X-Content-Type-Options: nosniff | Prevents MIME-type sniffing attacks |
| X-XSS-Protection: 1; mode=block | Enables browser-level XSS filter and blocks the page if an attack is detected |
| Referrer-Policy: no-referrer | Prevents referrer leakage to third-party URLs |
| Strict-Transport-Security | Forces HTTPS for 1 year including subdomains (HSTS) |
| Access-Control-Allow-Headers | Restricts allowed CORS headers to only what the framework needs |
<FilesMatch "^\.env$">
Require all denied
</FilesMatch>
<FilesMatch "\.system/.*\.log$">
Require all denied
</FilesMatch>
<FilesMatch "^\.system/storage/.*$">
Require all denied
</FilesMatch>
These rules ensure that even if a misconfiguration allows directory browsing, sensitive files can never be served to the public.
Header unset X-Powered-By
ServerSignature Off
Options -Indexes
Removes PHP version information from response headers and disables Apache's "Powered by" signature - making it harder for attackers to fingerprint your stack.
<LimitExcept GET POST PUT DELETE>
Require all denied
</LimitExcept>
Blocks non-standard HTTP methods (OPTIONS, TRACE, CONNECT, etc.) that are commonly used in reconnaissance and exploit attempts.
RewriteEngine on
RewriteRule ^favicon\.ico$ /.assets/logo.png [L]
RewriteRule ^robots\.txt$ /.assets/robots.txt [L]
RewriteRule ^sitemap\.xml$ /.assets/sitemap.xml [L]
RewriteRule ^assets/(.*)$ /.assets/$1 [L]
# Allow static asset files
RewriteCond %{REQUEST_URI} ^/.assets/.*\.(css|js|svg|mp4|ogg|mp3|jpeg|jpg|png|gif|ico|webp|xml|txt|json|csv)$ [NC]
RewriteRule .* - [L]
# Route everything else through PHP
RewriteRule ^(.*)$ .system/app.php [L,QSA]
All non-static requests are routed through .system/app.php, giving the framework complete control over routing, access checking and response handling.
SetEnv CONTENT_LENGTH 2
Limits request body size to 2 MB by default. Adjust this value in .htaccess if your application needs to handle larger file uploads.
The .htaccess file is generated once during phpshift new and is not automatically regenerated. Edit it manually to:
CONTENT_LENGTH for file upload pagesAfter editing, test your changes with Apache's config test:
C:/xampp/apache/bin/httpd.exe -t
PHPShift's framework engine includes built-in rate limiting at two levels: per-session (ID rate) and per-IP (IP rate). These are configured in .htaccess via environment variables read by .system/app.php.
# Request body size limit (MB)
SetEnv CONTENT_LENGTH 2
# Session lock duration after rate limit hit (seconds)
SetEnv LOCK_TIME 30
# Per-session rate: time window (seconds)
SetEnv IDR_PERIOD 10
# Per-session rate: max requests per window
SetEnv IDR_AMOUNT 10
# Per-IP rate: time window (seconds)
SetEnv IPR_PERIOD 60
# Per-IP rate: max requests per window
SetEnv IPR_AMOUNT 200
Tracks requests per session identifier. If a single user session makes more than IDR_AMOUNT requests within IDR_PERIOD seconds, the session is locked for LOCK_TIME seconds.
Default: max 10 requests per 10 seconds per session. Locks for 30 seconds on violation.
Use case: protects against AJAX-heavy pages accidentally hammering the server and catches scripted abuse using a stolen session.
Tracks requests per client IP address. If a single IP makes more than IPR_AMOUNT requests within IPR_PERIOD seconds, further requests from that IP are temporarily blocked.
Default: max 200 requests per 60 seconds per IP.
Use case: protects against scraping, brute force login attempts and DDoS from a single source.
The defaults are conservative. For applications with heavy front-end JavaScript activity (many App.call() requests per page), you may need to increase IDR_AMOUNT or IDR_PERIOD:
# Allow more requests for a data-heavy dashboard
SetEnv IDR_PERIOD 10
SetEnv IDR_AMOUNT 30
For public APIs that expect high volume from legitimate clients:
SetEnv IPR_PERIOD 60
SetEnv IPR_AMOUNT 1000
When a session hits the ID rate limit, it is locked for LOCK_TIME seconds. Legitimate users experience a brief pause before they can make requests again. Scripted attackers face a compounding delay.
CONTENT_LENGTH sets the maximum allowed request body in megabytes. The framework rejects requests exceeding this size before processing.
For pages with file uploads, increase this to match your expected maximum file size:
SetEnv CONTENT_LENGTH 10
Open .htaccess in your project root and adjust the SetEnv values. Changes take effect immediately - no Apache restart needed for .htaccess directives (as long as AllowOverride All is set in your virtual host configuration).
PHPShift's core workflow is a tight feedback loop: describe what you want, review the AI's output, confirm or retry. Understanding this loop helps you work efficiently and avoid frustration when output isn't quite right the first time.
1. Select a skill from the menu
2. Describe your task in plain language
3. PHPShift collects project context and sends it to the AI
4. AI generates structured file output
5. Files are written, DB changes staged
6. PHPShift shows you the result
7. You choose: Yes / Redo / No
The files remain on disk, database changes are committed (DB.clear()) and you're offered a Git commit prompt. This finalizes the skill run.
All changes roll back (files restored to pre-skill state, database reverted via DB.rollback()). PHPShift asks you to refine your description:
What would you like to change about your request?
You can add clarifying detail, correct a misunderstanding, or specify what was wrong with the previous attempt. The skill runs again with your updated description - often the AI gets it right on the second pass because it now has more specific guidance.
All changes roll back completely and you return to the main skill menu. No trace of the attempt remains.
The quality of AI output is directly tied to the quality of your description. Effective descriptions include:
Add a login page
Create a public login page with email and password fields, client-side
validation for email format, a "remember me" checkbox and a link to
a password reset flow. On successful login, redirect to /dashboard.
On failure, show an inline error message without page reload.
Match the styling of the existing welcome page.
Complex features often require multiple skill invocations:
1. Create > Page (basic structure)
2. Edit > Page (add specific interactive feature)
3. Edit > Page (refine styling)
4. Create > Translation (add language support)
Each run benefits from full project context, so the AI stays consistent across iterations even when you're building incrementally.
If a page has accumulated many small edits and feels inconsistent, consider Render > Page to regenerate it cleanly from its readme.md description, rather than continuing to patch with Edit > Page.
The patch system is what makes PHPShift's AI generation safe to experiment with. Every skill run is wrapped in a snapshot-and-rollback mechanism covering both filesystem and database changes, so a bad AI response never leaves your project in a broken state.
AI-generated code isn't always perfect on the first try. Without a safety net, a flawed generation could overwrite working files or insert bad data into your database with no easy way back. The patch system makes every skill run fully reversible.
Before a skill writes any files:
The Python DB module provides three key methods:
DB.reserve() - Called before a skill begins; starts tracking subsequent database operationsDB.clear() - Called when you confirm (Yes); finalizes all staged changes permanentlyDB.rollback() - Called when you reject (Redo/No); reverts all database mutations made since reserve()This means a db.sql migration that creates a table, inserts data, or alters a schema can be completely undone if you're not happy with the accompanying code.
| Category | Tracked |
|----------|---------|
| Page files (html/css/js/php/readme) | Yes |
| API files | Yes |
| Cron files | Yes |
| Database schema changes | Yes |
| .access file changes | Yes |
| .env new variable additions | Yes |
| Composer package installations | Yes (uninstall on rollback) |
| Git commits | No - commits happen only after final confirmation |
Read > Database queries execute immediately and are not part of the patch system, since they're read operations by default (though a destructive query you explicitly request will run for real)Read > Git commands execute immediately for the same reasonBecause of the patch system, you can be liberal with Redo. If a generated page is close but not quite right, don't manually fix it - describe the correction and let the AI regenerate. The rollback ensures no half-finished, inconsistent state accumulates from failed attempts.
If you ever need to manually verify rollback was clean, check your Git status - since uncommitted changes from a rejected skill run should leave your working directory exactly as it was before the skill started (assuming you commit after every accepted change, which PHPShift encourages).
PHPShift treats Git as a first-class part of the development workflow. Every confirmed skill run offers an immediate commit opportunity, keeping your history granular and meaningful without requiring you to leave the terminal.
During phpshift new, you're asked whether to initialize a Git repository. If yes, PHPShift runs git init and creates an initial .gitignore that excludes:
.env
.system/vendor/
Space/*.log
*.local
node_modules/
When you confirm a skill's output (select Yes), PHPShift immediately asks:
Want to commit these changes?
If you confirm, you're prompted for a commit message - PHPShift can suggest one based on the skill and your task description, or you can write your own.
git add .
git commit -m "Add user dashboard page with task stats"
After committing, if a remote is configured, PHPShift asks:
Want to push to remote?
Confirming runs git push against the current branch's tracked remote.
This pattern produces a clean, readable commit history where each commit corresponds to one logical AI-generated change - a new page, an edit, a translation addition. This makes it easy to:
The automatic commit/push flow handles the common case. For everything else - branching, merging, diffing, resetting, configuring remotes - use the Read > Git skill, which translates natural language into the appropriate Git command. See AI Skills → Read → Git for details.
While PHPShift doesn't enforce a specific Git workflow, a simple effective pattern for AI-paired development is:
main for small, low-risk changes (the patch system already protects you from bad generations)Read > Git to create a feature branch for larger multi-skill featuresmain once the feature is complete and testedIf your generated code introduces new sensitive files or build artifacts, update .gitignore manually or via Read > Git:
Add a .gitignore rule to exclude the new uploads/ directory
PHPShift's translation system makes internationalizing a PHP project a one-step AI operation instead of a manual, error-prone process of touching every layer of the stack by hand.
1. Build your page in your primary language (usually English)
2. Run Create > Translation, specify the target language
3. AI updates HTML, CSS, JS and PHP to support the new language
4. AI generates the Space/lng.{lang}.json file
5. Repeat for additional languages as needed
| Layer | Change |
|-------|--------|
| page.html | <translation languages="en,ka"> tag added; translate="key" attributes on text elements |
| style.css | Language-switcher UI styles added |
| script.js | Hardcoded strings wrapped in App.lng(key, defaultText, vars) |
| code.php | Hardcoded strings wrapped in App::lng(key, defaultText, vars) |
| Space/lng.{lang}.json | New file with all translated key-value pairs |
The <translation> tag tells the framework which languages a page supports and renders a language switcher UI:
<translation languages="en,ka,de"></translation>
The framework reads the visitor's preferred language (from a cookie, URL parameter, or browser header) and serves the matching Space/lng.{lang}.json values wherever translate attributes or lng() calls appear.
Static (HTML):
<h1 translate="page_title">Dashboard</h1>
Dynamic (JavaScript):
const msg = App.lng('items_count', '{count} items found', {count: total});
Dynamic (PHP):
$subject = App::lng('email_subject', 'Welcome, {name}!', ['name' => $username]);
The {variable} placeholder syntax works consistently across both JS and PHP lng() calls.
{
"page_title": "მთავარი გვერდი",
"items_count": "{count} ნივთი ნაპოვნია",
"email_subject": "კეთილი იყოს თქვენი მობრძანება, {name}!"
}
You don't need to translate everything at once. Run Create > Translation per page, per language, as needed. The skill detects existing translation infrastructure on a page and extends it rather than duplicating work.
When you add new features to an already-translated page (via Edit > Page), new hardcoded strings may appear without translation keys. Run Edit > Translation to scan for and translate any newly introduced text.
Use Delete > Translation to remove a language's lng.*.json file. Note that this does not automatically strip the language from <translation languages="..."> tags across pages - follow up with Edit > Page if you want that cleanup.
PHPShift manages cron job execution during local development through a Python background thread and provides the scaffolding needed for production cron setup. This page covers the operational side of working with crons day to day.
When you run phpshift start and cron files exist in Crons/, you're asked:
Want to start cron jobs?
If you confirm, a background Python thread starts, calling:
php cron -auto
once every 60 seconds for the lifetime of your session.
You can also control this manually from the skill menu at any time:
More > StartCrons - Start the background cron thread
More > StopCrons - Stop the background cron thread
The menu dynamically shows the relevant option (Start vs Stop) based on current thread state.
The cron entry point script, when called with -auto, iterates every .php file in Crons/ and executes each one. Each cron file contains its own internal time-check guard (e.g., if (date('H:i') !== '00:00') exit;), so calling all crons every minute is safe - only the ones whose schedule matches the current time actually perform work.
To test a specific cron job without waiting for its schedule or running the full auto cycle:
php cron sendDigest
This runs Crons/sendDigest.php directly, ignoring its internal time-check guard - useful for debugging.
Cron jobs typically log their activity to files in Space/:
file_put_contents(__DIR__ . '/../Space/digest.log', date('Y-m-d H:i:s') . " - Sent to " . count($users) . " users\n", FILE_APPEND);
Use Read > Log to have the AI analyze these logs and surface issues without manually scanning through raw text.
PHPShift's background thread is a development convenience only - it does not run when PHPShift's CLI is closed. For production, configure a real system cron job on your server:
# crontab -e
* * * * * php /var/www/myapp/cron -auto >> /var/www/myapp/Space/cron-system.log 2>&1
This mirrors the development behavior: called every minute, each job self-determines whether it's time to run.
| Pattern | Guard |
|---------|-------|
| Every minute | (no guard) |
| Every 5 minutes | if ((int)date('i') % 5 !== 0) exit; |
| Hourly | if (date('i') !== '00') exit; |
| Daily at specific time | if (date('H:i') !== '03:00') exit; |
| Weekly on a day | if (date('D H:i') !== 'Mon 08:00') exit; |
| Monthly on a date | if (date('d H:i') !== '01 00:00') exit; |
Space/*.log files for output via Read > Logphp cron {filename}More > StartCrons thread is actually running if testing live auto-execution