# **SartajPHP: The Event-Oriented PHP Framework** **A Comprehensive Developer Guide** --- ## **Table of Contents** ### **Part I: Foundations** 1. **Introduction to SartajPHP Philosophy** 2. **Architecture and Core Concepts** 3. **Installation and Project Setup** 4. **Understanding the Event-Oriented Paradigm** ### **Part II: Development Fundamentals** 5. **Project Structure and Configuration** 6. **Application Lifecycle and PageEvents** 7. **FrontFile System: HTML with Superpowers** 8. **Components: Server-Side UI Building Blocks** ### **Part III: Advanced Patterns** 9. **Fusion Attributes and Execution Flow** 10. **Expression Tags and Dynamic Evaluation** 11. **Multi-Application Architecture** 12. **NativeGate and Real-Time Systems** ### **Part IV: Practical Development** 13. **Building Web Applications** 14. **Forms, Validation, and Database Operations** 15. **AJAX and Client-Server Communication** 16. **Security and Performance Optimization** ### **Part V: Reference** 17. **Component Reference Guide** 18. **Best Practices and Patterns** 19. **Troubleshooting and Debugging** 20. **Migration and Integration Strategies** --- # **Chapter 1: Introduction to SartajPHP Philosophy** SartajPHP is not MVC Framework, It is Event oriented PHP Framework. A typical SartajPHP Project constructed with BasicGate (Web App's Gate), Front File, Master File, Componets and Front Place. SartajPHP don't use Controller and MVC pattern. # **SartajPHP Gate Architecture** ### *Gate‑Driven Event Architecture (GDEA)* SartajPHP uses a **Gate‑Driven Event Architecture (GDEA)** instead of traditional MVC. In this architecture, every application is represented by a **Gate**, and every request is processed as a sequence of **events**. A Gate acts as the **entry point**, **lifecycle controller**, and **event processor** for the application. All browser, terminal, or socket requests must pass through a Gate before any output is generated. This design provides: - A unified request entry point - A clean event‑oriented workflow - A predictable lifecycle - A flexible structure for different application types --- ## **What Is a Gate?** A **Gate** is a class that: - Receives the incoming request - Interprets URL → Event + Parameters - Executes lifecycle events - Executes page events - Processes data - Renders output (HTML, JSON, text, etc.) - Returns the final response A Gate replaces the traditional “Controller” or “App” class found in MVC frameworks. --- ## **Gate Types** SartajPHP provides multiple Gate types for development of different type of application: | Gate Type | Purpose | |----------|----------| | **BasicGate** | Standard web applications (HTML output) | | **NativeGate** | WebSocket or long‑running applications | | **ConsoleGate** | Terminal‑based applications | All custom Gates must extend one of these base classes. Example: ```php class Index extends \Sphp\tools\BasicGate {} ``` --- ## **Gate Naming Convention** To avoid confusion with the similar file extension “php” all Gate files must use the **.gate.php** suffix in file name. So we can easily identified Gate class files. ### **Class Name** ``` Index Admin Api ``` ### **File Name** ``` Index.gate.php Admin.gate.php Api.gate.php ``` This ensures: - Clear categorization - Clean autocomplete grouping - Predictable file structure - Zero naming collisions with “App” --- ## **Request Flow** The request lifecycle in SartajPHP follows this sequence: ``` Browser / Terminal Request ↓ SartajPHP Framework Core ↓ URL Translator (index-myevent-eventpara.html → GateName + EventName + EventParams) ↓ Gate Loader ↓ Gate Object Created ↓ Lifecycle Events Executed ↓ Page Events Executed ↓ Data Processing (optional) ↓ Front File + Master File Rendering (optional) ↓ Output Returned to Browser / Terminal ``` --- ## **URL → Gate Translation** SartajPHP uses a structured URL format: ``` index-myevent-eventpara.html ``` This is translated into: - **Gate Name:** `Index` - **Event Name:** `myevent` - **Event Parameter:** `eventpara` This translation is handled internally by the framework. --- ## **Gate Lifecycle** Lifecycle events trigger for all requests. So you can code here which is required for every request like check authentication on onstart event. So all Lifecycle events are used for configuration in sequence. Most of Gate only need to implement onstart event handler. Every Gate follows a predictable lifecycle: 1. **onstart()** Trigger On Start after construct, Initialize environment, Create Front File Objects, Check Authentication, Check Permission. 2. **onready()** TriggerAfter onstart. Update or change Components settings. 3. **onrun()** Trigger After onready and before process any PageEevnt 4. **onrender()** Trigger After onrun, PageEvents and before output send to browser. Add extra on demand CSS,JS File Links or codes Developers may override any lifecycle method. --- ## **Page Events** A Gate can handle multiple **page events**, each handle to a specific browser URL in BasicGate and NativeGate and command line arguments in ConsoleGate. Example: ```php /** url=gate-login-evtp.html */ public function page_event_login($evtp) { // Handle login } ``` Events are triggered automatically based on the URL translation. --- ## **Rendering System** Gates may optionally use: - **Front File** *.front files (page template) - **Master File** *.mast.php files inside masters folder (layout template) Rendering is handled by the Gate’s internal rendering engine. --- ## **Why Gate Architecture?** The Gate‑Driven Event Architecture provides several advantages: ### ✔ No MVC complexity No controllers, models, or views to manage. ### ✔ Event‑oriented Everything is an event — simple and predictable. ### ✔ Unified entry point Each application task has a single Gate controlling its lifecycle. ### ✔ Flexible output HTML, JSON, text, or WebSocket responses. ### ✔ Clean naming No confusion between “php” files and “php” Gate (class). ### ✔ Perfect for multi‑application frameworks Web, console, and socket apps share the same architecture. --- ## **Example Gate** ```php frtMain = new \Sphp\tools\FrontFile($this->mypath . "/fronts/index_main.front"); } /** Browser send Get Request to landing URL of Gate, Trigger New Event*/ public function page_new() { $this->frtMain->addMetaData('title', 'Welcome'); $this->setFrontFile($this->frtMain); } } ``` --- ## **Conclusion** SartajPHP’s **Gate‑Driven Event Architecture** provides a clean, modern, event‑oriented alternative to MVC. By using Gates as the core entry point for all applications, the framework achieves: - Simplicity - Predictability - Flexibility - High readability - Strong architectural identity This pattern is unique to SartajPHP and forms the foundation of its design philosophy. 1. BasicGate :- A Gate (*.gate.php file) where you can write your Business Logic as OOP Programming and Handle Browser Requests landed as Events. 2. Front File :- A HTML File (*.front file) with some special Tags and Attributes which is processed on Server. You can easily convert any HTML file into Front File. 3. Components :- A (*.php file) PHP Classes extends with \Sphp\tools\Component parent. Front File convert HTML Tags with runat="server" into Component Objects. Component can create a complex HTML,CSS,JS output without writing a single line of code. You can use them in Front File as HTML Tag. 4. Master File :- A (*.mast.php) PHP File which call SartajPHP API to get Generated Dynamic output from App,Front File,PHP and Front Places. Master File include head and body tag of HTML page to send to browser. First MasterFile gather all dynamic output and then place into HTML Tags accoridng to layout. Master File is actually a combination of header and footer in other frameworks + Front Places. 5. Front Places :- Front Place (*.place.front or *.place.php file) is a place in Master File which generate dynamic output and print in Master File. Front File and PHP Class extends with Sphp\tools\FrontPlace class can be used as Front Place. For Example A Slider in website can be created with use of Front Place and Show and Hide with Gate and easily control HTML output send to browser without creating a separate files. ## **1.1 The Evolution Beyond MVC** In the landscape of PHP frameworks, developers have long been confined to the Model-View-Controller (MVC) pattern. While MVC provides structure, it often imposes rigid constraints that don't align with modern web development needs. Enter SartajPHP—a revolutionary approach that reimagines how PHP applications are structured and executed. SartajPHP isn't just another framework; it's a paradigm shift. It replaces controller-based routing with an **event-oriented architecture** where every user interaction becomes a discrete event handled by specialized application units. This fundamental change unlocks unprecedented flexibility, performance, and developer productivity. ## **1.2 Core Philosophy: Event-Oriented Design** At the heart of SartajPHP lies a simple but powerful principle: **Every request is an event, not a route.** Traditional frameworks map URLs to controller methods: ``` GET /users/profile → UserController::profile() POST /users/update → UserController::update() ``` SartajPHP translates URLs into PageEvents: ``` GET index.html → Index.gate.php::page_new() POST index.html → Index.gate.php::page_submit() GET index-profile-123.html → Index.gate.php::page_event_profile("123") ``` This event-oriented approach offers several advantages: ### **1.2.1 Natural Request Handling** Human interactions with websites are inherently event-driven: "click submit," "view details," "delete item." SartajPHP mirrors this natural model in code, making application logic more intuitive and maintainable. ### **1.2.2 Decoupled Architecture** Gate in SartajPHP are self-contained unit that declare what events they handle. There's no central routing table to maintain, no fragile route definitions that break when refactoring. ### **1.2.3 Multiple Application Types** Because each Gate Object is independent, a single SartajPHP project can host: - Traditional web application - API-only services - Real-time WebSocket services - Legacy systems (WordPress, Laravel modules) - Background workers - All running simultaneously, isolated yet interoperable ## **1.3 The Three Pillars of SartajPHP** ### **Pillar 1: Pure HTML Templates** SartajPHP's FrontFile system uses **100% valid HTML** as templates. No custom templating language to learn, no special syntax that breaks HTML validation. Designers can work with standard HTML files, while developers enhance them with special attributes that add server-side behavior only when needed. **Example: Progressive Enhancement** ```html ``` The same HTML file serves both as a static prototype and a fully dynamic application view. ### **Pillar 2: Component-Based UI** UI elements in SartajPHP are **Components**—server-side objects that bind to HTML elements. A Component isn't just a widget; it's a complete unit with: - Server-side validation - Database binding - Lifecycle events - Client-server synchronization - Permission and authentication control Components automatically handle their own concerns, reducing boilerplate and eliminating common security oversights. ### **Pillar 3: Multi-Application Environment** Unlike single-application frameworks, SartajPHP treats each functional Gate unit as an independent application. This enables: **Microservices Architecture in a Single Codebase:** ``` /project/ ├── apps/ │ ├── Blog.gate.php # Blog system │ ├── Shop.gate.php # E-commerce │ └── Api.gate.php # JSON API ├── appsn/ │ └── Notifications.gate.php # WebSocket service └── reg.php # Registry connects them all ``` Each Gate has its own lifecycle, configuration, and resources, yet they share the same runtime and can communicate seamlessly. ## **1.4 Performance by Design** SartajPHP achieves exceptional performance through architectural decisions: ### **1.4.1 Minimal Runtime Overhead** The framework loads only what's needed for the current request. A typical SartajPHP request completes in **20-50ms**, compared to 150-1200ms for heavier frameworks. ### **1.4.2 Intelligent Caching** The caching system operates at multiple levels: - **FrontFile compilation cache** (parsed templates) - **Component initialization cache** - **PageEvent output cache** - **Database query cache** Caching rules are declarative and fine-grained: ```php // Cache all index Gate responses for 1 hour addCacheList("index", 3600); // Cache specific event with parameter addCacheList("index", "view", 1800, "cep"); ``` ### **1.4.3 Built-in Optimization** - Automatic CSS/JS minification and bundling - Intelligent asset loading (CSS in head, JS at bottom) - Duplicate prevention for included resources - Gzip compression and HTTP/2 push support ## **1.5 Security Architecture** Security isn't bolted on—it's built into SartajPHP's foundation: ### **1.5.1 No URL-to-File Mapping** Traditional frameworks expose their structure through URLs: ``` /user/profile → /app/controllers/UserController.php ``` SartajPHP uses Gate Name that hide the actual file path structure: ``` user-profile.html → Gate Loader "user" → /apps/User.gate.php user-profile.html → Gate Loader "user" → /apps/users/UserMember.gate.php ``` Attackers cannot infer paths or probe for files. ### **1.5.2 Component-Level Security** Each Component can declare permission requirements: ```html
Delete
``` If permission checks fail, the Component (and its contents) are completely removed from output—not just hidden with CSS. ### **1.5.3 Automatic Input Handling** Components automatically sanitize and validate their values. There's no need for manual `htmlspecialchars()` or SQL escaping when using built-in data binding. ## **1.6 When to Choose SartajPHP** SartajPHP excels in several scenarios: ### **Ideal Use Cases:** 1. **Modern Web Applications** requiring real-time features 2. **Legacy System Integration** where multiple technologies must coexist 3. **Rapid Prototyping** with immediate client review capability 4. **Microservices Architecture** in a monolithic deployment 5. **Progressive Web Apps** with offline capabilities 6. **Real-Time Dashboards** and monitoring systems ### **Comparison with Other Frameworks:** | Aspect | Laravel | Symfony | SartajPHP | |--------|---------|---------|-----------| | **Learning Curve** | Moderate | Steep | Shallow (HTML-first) | | **Performance** | Good | Good | Excellent | | **Real-time Support** | Requires packages | Requires packages | Built-in | | **Multiple Apps** | Not designed for | Possible but complex | Fundamental feature | | **Template System** | Blade (custom) | Twig (custom) | Pure HTML | | **Architecture** | MVC | MVC | Event-Oriented | ## **1.7 The Developer Experience** ### **1.7.1 Back-to-Front Development** Developers can work in their preferred direction: - **Back-to-Front**: Start with PHP logic, add UI later - **Front-to-Back**: Design HTML first, connect logic later - **Middle-Out**: Build Components, integrate both sides ### **1.7.2 Tools and Ecosystem** - **SphpDesk**: Integrated development environment - **VS Code Extension**: Intelligent code completion - **Browser DevTools Integration**: Debug Components in console - **CLI Tools**: Project scaffolding and management ### **1.7.3 Progressive Complexity** Start simple and add sophistication as needed: ```php // Phase 1: Simple page class Hello extends BasicGate { public function page_new() { echo "Hello World"; } } // Phase 2: Add FrontFile class Hello extends BasicGate { private $frtMain; public function onstart() { $this->frtMain = new FrontFile($this->mypath . "/hello_main.front"); } public function page_new() { $this->setFrontFile($this->frtMain); } } // Phase 3: Add Components and events class Hello extends BasicGate { // ... plus Components and page_event handlers private $frtMain; public function onstart() { $this->frtMain = new FrontFile($this->mypath . "/hello_main.front"); } public function page_new() { $this->setFrontFile($this->frtMain); } /** * Handle start render event of div1 Component. */ public function comp_div1_on_startrender($args){ $str1 = ""; // Loop All Children of Form Components foreach($this->frtMain->frmcheck->getAllChildren() as $i=> $child){ $str1 .= " Child $i : " . get_class($child) . " id = " . $child->id . "
"; } // set as Inner HTML to element of div1 $args["element"]->setInnerHTML($str1); } } ``` ## **1.8 Real-World Impact** Companies adopting SartajPHP report: - **70% reduction** in boilerplate code - **60% faster** development cycles for new features - **40% reduction** in server costs due to performance 70- **90% decrease** in XSS and SQL injection vulnerabilities - **Seamless integration** of legacy systems without rewriting ## **1.9 Chapter Summary** SartajPHP represents a fundamental rethinking of PHP framework architecture. By embracing event-oriented design, pure HTML templates, and multi-application architecture, it solves long-standing problems in web development: 1. **Complexity Management** through isolated, focused applications 2. **Team Collaboration** through HTML-first development 3. **Performance** through minimal, optimized runtime 4. **Security** through architecture, not just practices 5. **Flexibility** to handle any project type or scale In the following chapters, we'll explore how to harness these principles to build robust, maintainable, and high-performance applications. -- # **Chapter 2: Architecture and Core Concepts** ## **2.1 The Layered Architecture** SartajPHP employs a sophisticated layered architecture that separates concerns while maintaining tight integration. Understanding these layers is crucial to mastering the framework. ### **2.1.1 Runtime Layer (`Score/`)** The innermost layer contains the framework engine: - **Event Dispatcher**: Translates requests into PageEvents - **Gate Loader**: Resolves and loads application's Gates - **Component Factory**: Creates and manages Components - **Cache Manager**: Handles multi-level caching This layer is framework-managed and rarely accessed directly. ### **2.1.2 Shared Resource Layer (`res/`)** The shared layer provides reusable assets across projects: ``` /res ├── Slib/ # Shared libraries, Components ├── jslib/ # JavaScript libraries (jQuery, Bootstrap, etc.) ├── components/ # Components,uikitdef, MasterFiles ├── plugin/ # Shared plugins └── sample/ # Example projects ``` Projects reference these resources but don't modify them, ensuring upgradability. ### **2.1.3 Project Layer** Your application code lives here: ``` /project/ ├── apps/ # Web applications (*.gate.php files) ├── appsn/ # NativeGate and ConsoleGate ├── masters/ # Project-specific masters ├── plugin/ # Project plugins └── cache/ # Runtime cache ``` Each layer has clear responsibilities and interaction rules. ## **2.2 The Request-Response Cycle** Understanding how SartajPHP processes requests is fundamental to effective development. ### **2.2.1 Complete Request Flow** ``` Browser Request ↓ Web Server (Apache/Nginx) ↓ .htaccess → Rewrite to start.php ↓ SartajPHP Engine Initialization ├── Load core runtime ├── Parse CLI arguments (if console) ├── Set up error handling ↓ Router Analysis ├── Extract Gate from URL ├── Identify event type ├── Parse event parameters ↓ GateLoader Resolution ├── Check reg.php for Gate File registration ├── Load corresponding *.gate.php file ↓ prerun.php Execution ├── Security headers ├── CORS policies ├── Global initialization ↓ Gate Instantiation ├── Create Gate object ├── Trigger onstart() event ↓ FrontFile Processing (if applicable) ├── Parse .front file ├── Create Components ├── Execute Parse Phase Fusion ↓ PageEvent Execution ├── Trigger page_new() or page_event_*() ├── Handle form submissions ├── Process business logic ↓ Render Phase ├── Execute Render Phase Fusion ├── Process runtime attributes ├── Generate HTML output ↓ MasterFile Integration ├── Apply master template ├── Inject menus, headers, footers ↓ Cache Check/Store ├── Check if response is cacheable ├── Store in cache if needed ↓ Browser Response ``` ### **2.2.2 URL Translation Rules** SartajPHP uses a consistent URL pattern: ``` --.html ``` **Examples:** ``` index.html → Gate: index, Event: new index-view-5.html → Gate: index, Event: view, Parameter: 5 shop-product-category-12.html → Gate: shop, Event: product, Parameter: category-12 ``` The hyphen is always the separator. There are no query strings (?param=value) in the SartajPHP URL model—all parameters are part of the URL path. If you use query strings then it will be deal as same as in other php applications and no effect to trigger or chnage event on Server. ## **2.3 Application Types** SartajPHP supports multiple application types, each optimized for different use cases. You can also develop your own Gate Type to extend with Parent Gate Type or from scratch. ### **2.3.1 BasicGate (Web Applications)** The most common type, extending `Sphp\tools\BasicGate`: ```php class My extends \Sphp\tools\BasicGate { private $frtMain; public function onstart() { $this->frtMain = new FrontFile($this->mypath . "/fronts/my_main.front"); } public function page_new() { $this->setFrontFile($this->frtMain); } } ``` **Characteristics:** - Runs under Apache/Nginx - Handles HTTP/HTTPS requests - Supports AJAX communication - Uses FrontFiles for UI ### **2.3.2 NativeGate (Real-Time Services)** Extends `Sphp\tools\NativeGate` for long-running processes: ```php class ChatServer extends \Sphp\tools\NativeGate { public function onwscon($wsconobj) { // Handle new WebSocket connection $this->JSServer->addJSONReturnBlock("New User Entered " . $wsconobj->getConnectionId()); // Send Data to All connections but leave new Connection $this->sendOthers(); } public function page_event_mesaage($evtp) { // extra data submit by browser via Web Socket $bdata = $this->Client->request("bdata"); $msg = $bdata["msg"]; $user_name = $bdata["user_name"]; $this->JSServer->addJSONHTMLBlock("msgbox","@{$user_name} :- $msg"); // Send Data to All connections but leave sender Connection $this->sendOthers(); } public function onwsdiscon($wsconobj) { $this->JSServer->addJSONReturnBlock("User Leave " . $wsconobj->getConnectionId()); // Send Data to All connections $this->sendAll(); } } ``` Little Long Sample ```php class ChatServer extends \Sphp\tools\NativeGate { public function onstart() { // Create Python Gate as Child Process on start App $pythonpath = __DIR__ . "/env/python.exe"; $pythonapp = __DIR__ . "/main.py"; $this->createProcess($pythonpath . " -u " . $pythonapp); /** Make Global Gate mean handle All requests by only one process. The Manager of process * of Gate is the mainConnection which send first request or setup by * gate. For change or set new Manager use setGlobalAppManager. By Default * Gate create one process per connection */ $this->setGlobalApp(); } public function page_event_s_oncreateprocess($evtp,$bdata){ // child process is ready // send to data to Socket Component message handler $this->JSServer->addJSONReturnBlock("Child Process is Ready and Create By:- " . $this->mainConnection->getConnectionId()); // send to only current connection of WS and It is always Main Connection $this->sendTo(); } public function onwscon($wsconobj) { // Handle new WebSocket connection $this->JSServer->addJSONReturnBlock("New User Entered " . $wsconobj->getConnectionId()); // Send Data to All connections except New Connection $this->sendOthers(); } public function onwsdiscon($wsconobj) { $this->JSServer->addJSONReturnBlock("User Leave " . $wsconobj->getConnectionId()); // Send Data to All connections $this->sendAll(); } public function onconsole($data,$type) { // debug invalid data if($type == ""){ $this->JSServer->addJSONReturnBlock("Unknown Data: " . $data); // response to current request only $this->sendTo(); } } public function onquit(){ $this->JSServer->addJSONReturnBlock("Gate Quit, Bye Bye"); // send to All WS connections bind with this App $this->sendAll(); } // call on quit of child process // only child process is use with life of gate. so quit with child process public function oncquit(){ // enable start button $this->JSServer->addJSONJSBlock("$('#btnstart').prop('disabled',false)"); // Return Message to Socket Component $this->JSServer->addJSONReturnBlock("Child Quit"); $this->sendTo(); // Exist The Gate also with quit of child process $this->exitMe(); } } ``` **Characteristics:** - Requires SphpServer runtime - Maintains persistent connections - Can spawn child processes - No HTTP request/response cycle - Supports only WebSocket communication ### **2.3.3 ConsoleGate (CLI Applications)** ConsoleGate can run with SphpDesk as Stand Alone Script file. You can use this as alternative of bash file. To run this file you need SartajPHP framework for desktop install with npm or download setup from sartajphp.com npm install -g sphpdesk then run this file:- ``` sphpdesk script ./compile.php ``` or as part of project and run with start.php with php. gate = Gate, evt = Event and evtp = Event Parameter You can also pass your own custom command lines start with "--" ``` php ./start.php --gate index --evt page --evtp contact --myparam "Hello World" ``` Extends `Sphp\tools\ConsoleGate` for command-line tools: ```php class BackupTool extends \Sphp\tools\ConsoleGate { public function page_new() { $this->sendMsg("Starting backup..."); // Backup logic } } ``` Compile C++ Project ```php class Compile extends \Sphp\tools\ConsoleGate { public function onstart(){ //$this->disableStdout(); // print debug info $this->enableStdout(); } public function page_new(){ $this->proj = $this->consoleReadArgument("--proj"); if($this->proj != ""){ $this->compileproj($this->proj); }else{ $this->sendMsg("--proj argument not exist",'e'); } } public function compileproj($proj){ $gccp = ""; $this->callf('which g++', 'Check G++', function($msg) use (&$gccp){ $gccp = $msg; }); $str1 = $this->findAppendLib(); $compilecmd = 'g++ -o sbrowser.exe ./sbrowser/src/sbrowser.cpp -std=c++14 ' . $str1[0] . $str1[1] . $str1[2]; $this->sendMsg('compile ' . $compilecmd); $this->calla($compilecmd, 'Compling', function($msg){ $this->sendMsg($msg); },function($msg){ $this->sendMsg($msg); }); } private function findAppendLib(){ $libsrc = __DIR__ . '/' . $this->proj . '/lib'; $extralibflags = ""; $strinclude = ""; $strlink = ""; $dir = new DIR(); $liblist = $dir->directoryCount($libsrc); //$total = count($filelist); foreach ($liblist as $key => $value) { $slib = json_decode(file_get_contents(realpath("{$libsrc}/$value/slib.json")),true); $extralibflags .= " " . $slib[$this->os][$this->arch]; $strinclude .= " -I" . realpath("{$libsrc}/$value/include"); $strlink .= " -L" . realpath("{$libsrc}/$value/{$this->os}/{$this->arch}") . " -l{$value}"; } return [$strinclude,$strlink,$extralibflags]; } } ``` **Characteristics:** - Runs from command line - No web server required - Outputs to console - Can be scheduled via cron ## **2.4 Component Architecture** Components are the building blocks of SartajPHP applications. They bridge the HTML presentation layer with PHP logic. ### **2.4.1 Component Anatomy** Each Component consists of two parts: 1. **Component Object** (PHP class) - Server-side logic - Validation rules - Database binding - Lifecycle methods 2. **NodeTag** (HTML representation) - Tag name and attributes - Parent/child relationships - Rendering control ### **2.4.2 Component Lifecycle** ``` Component Creation ├── HTML parsed, runat="server" detected ├── Component class instantiated ├── NodeTag bound to Component ↓ Parse Phase Events ├── oninit() - Component initializing ├── oncreate() - Component created ↓ Gate Interaction ├── Gate can modify Component via Fusion ├── Component processes posted values ↓ Render Phase Events ├── onprerender() - Before HTML generation ├── onrender() - Generating HTML ├── onpostrender() - After HTML generation ↓ HTML Output ├── Component may output multiple HTML elements ├── Children Components rendered recursively ``` ### **2.4.3 Component Types** **Built-in Components:** - **Form Components**: TextField, TextArea, Select, CheckBox, Radio - **File Components**: FileUploader, ImageUploader - **Display Components**: Alert, Title, DataGrid, Pagination - **Layout Components**: Include, ForLoop, IfCondition **Custom Components:** Use \Sphp\tools\Component class as parent class to create custom Component. Developers can create reusable Components with custom behavior: ```php namespace MyApp\Components; class StarRating extends \Sphp\tools\Component { public function onrender() { $rating = $this->getValue(); $stars = str_repeat('★', $rating) . str_repeat('☆', 5 - $rating); $this->element->setInnerHTML($stars); } } ``` ## **2.5 FrontFile System Architecture** The FrontFile system is where SartajPHP's "pure HTML" philosophy comes to life. ### **2.5.1 FrontFile Processing Stages** ``` FrontFile Loading ├── Read .front file ├── Parse HTML into DOM tree ↓ NodeTag Creation ├── Each HTML element becomes NodeTag ├── Attributes parsed and stored ↓ Component Detection ├── runat="server" tags identified ├── Component objects created ↓ Attribute Processing ├── Fusion attributes (fui-, fun-, fur-) ├── Runtime attributes (runas, runcb) ├── Helper attributes evaluated ↓ Expression Tag Resolution ├── ##{ }# tags processed ├── Execution timing based on context ↓ HTML Generation ├── NodeTags convert to HTML ├── Components inject their output ``` ### **2.5.2 The Three Attribute Categories** 1. **Runtime Attributes** (`runat`, `runas`, `runcb`) - Control how tags are processed - Determine if Components are created 2. **Fusion Attributes** (`fui-*`, `fun-*`, `fur-*`) - Call Component methods - Control execution timing 3. **Helper Attributes** (`sphp-cb-*`, `dtable`, `dfield` , `id`, `path` custom attributes) - Provide data to Components - Used by Components for configuration ## **2.6 Event System** SartajPHP's event system operates at multiple levels. ### **2.6.1 PageEvents** URL-triggered events in Applications: ```php // URL: gate.html public function page_new() { // Default Event } // URL: gate.html (POST with form) public function page_submit() { // Form submission } // URL: gate-action-param.html public function page_event_action($evtp) { // $evtp contains "param" } ``` ### **2.6.2 Lifecycle Events** Gate's Life Cycle Events: ```php public function onstart() { // Gate initialization } public function onready() { // Gate ready to process } public function onrun() { // Gate execution starting } public function onrender() { // Gate execution finished and ready to send output } ``` ### **2.6.3 Component Events** Component-specific events: ```php // In FrontFile: public function comp_compname_on_init($evtp) { // Component initialization } ``` ### **2.6.4 FrontFile Events** ```php public function onfrontinit($frontobj) { // Front File Initialized } public function onfrontprocess($frontobj) { // Front File ready to process } ``` ### **2.6.5 WebSocket Events** (NativeGate only) ```php public function onwscon($conobj) { // WebSocket connection opened } public function onwsdiscon($conobj) { // WebSocket connection closed } ``` ## **2.7 Data Flow Patterns** Understanding data flow is crucial for building efficient applications. ### **2.7.1 Request Data Access** **Never use superglobals directly:** ```php // ❌ WRONG $name = $_POST['name']; // ✅ CORRECT $name = $this->Client->post('name'); // or $name = SphpBase::sphp_request()->post('name'); ``` **Component values are automatic:** ```html ``` ```php // Gate - value already available $name = $this->front->getComponent('txtName')->value; ``` ### **2.7.2 Database Operations** SartajPHP provides multiple database interaction patterns: **1. Component Binding (Automatic):** ```html ``` ```php // Auto-generates SQL and Insert, based on Components binding children of form2 $this->page->insertData($form2); ``` **2. Direct Database Access:** ```php $db = SphpBase::dbEngine(); $db->connect(); // use comp.php (Company) file settings $result = $db->executeQuery("SELECT * FROM users"); ``` **3. ORM-like Patterns:** ```php $user = new UserModel(); $user->name = $this->Client->post('name'); $user->save(); ``` ### **2.7.3 Response Patterns** **HTML Responses:** ```php $this->setFrontFile($this->front); // Render FrontFile ``` **AJAX/JSON Responses:** ```php $this->JSServer->addJSONReturnBlock(["status" => "success"]); $this->JSServer->addJSONHTMLBlock("result", "

Updated

"); ``` **Redirects:** ```php $this->page->forward(getGateURL("dashboard")); ``` ## **2.8 Cache Architecture** SartajPHP's caching operates at multiple levels for maximum performance. ### **2.8.1 Cache Levels** 1. **FrontFile Cache**: Parsed .front files stored as PHP objects 2. **Component Cache**: Initialized Components with default values 3. **Output Cache**: Complete HTML responses 4. **Database Cache**: Query results (optional) ### **2.8.2 Cache Configuration** **Project-level (`cachelist.php`):** ```php // Cache all index app responses for 1 hour addCacheList("index", 3600); // Cache specific Gate + event addCacheList("blog-view", 1800, "ce"); // Cache with Gate + event + event parameter addCacheList("shop-product-shirt", 3600, "cep"); // Cache only match event from any Gate and with any event parameter addCacheList("info", 3600, "e"); ``` **Programmatic caching:** ```php // Check cache if (!isRegisterCacheItem("index")) { addCacheList("index", 3600); } ``` ### **2.8.3 Cache Invalidation** Automatic invalidation occurs when: - FrontFile changes (file modification time) - Component configuration changes - Database tables are modified (with proper tagging) Manual invalidation: ```php clearCacheItem("blog-view"); ``` ## **2.9 Security Architecture Deep Dive** ### **2.9.1 Input Validation Layers** 1. **Component-Level Validation**: ```html ``` Validation occurs automatically before `page_submit()` proceeds. 2. **Type Conversion**: ```php $intValue = (int)$this->Client->post('number'); $safeHtml = $this->Client->request('htmlcontent'); // Auto-sanitized ``` 3. **Database Protection**: ```php // Component binding automatically escapes $this->page->insertData($form2); // Manual queries use parameterized statements $insertid = $this->dbEngine->runSQL("users", ["name"=>$name]); // or $sql = $this->dbEngine->insertSQL(["name"=>$name],"users"); $insertid = $this->dbEngine->executeQueryQuick($sql); ``` ### **2.9.2 Output Protection** **Automatic Context-Aware Escaping:** ```html

##{$userInput}#

##{raw:$trustedHtml}#

``` **Content Security Policy:** ```php // In prerun.php $policy = SphpBase::sphp_response()->getSecurityPolicy( "https://*.trusted.com 'self'" ); SphpBase::sphp_response()->addSecurityHeaders($policy); ``` ## **2.10 Performance Optimization Patterns** ### **2.10.1 Lazy Loading** Components and resources load only when needed. Resources renderonce="true" ignore on AJAX request and load in only full normal load request: ```html ``` ### **2.10.2 AJAX Response Merging** Multiple AJAX response can be batched: ```php // Server-side $this->JSServer->addJSONHTMLBlock("alert1", "Response1"); SphpBase::JSServer->addJSONJSBlock("$('#alert1').css('background-color','#FF0000');"); // Framework may batch these automatically ``` ### **2.10.3 Progressive Enhancement** Start with basic HTML, enhance with Components: ```html
``` ## **2.11 Chapter Summary** SartajPHP's architecture represents a thoughtful balance between power and simplicity. By understanding these core concepts: 1. **Layered Architecture** ensures separation of concerns while maintaining integration 2. **Event-Oriented Design** makes applications more intuitive and maintainable 3. **Component System** reduces boilerplate and improves security 4. **Multiple Application Types** provide flexibility for any project need 5. **Built-in Performance Optimizations** deliver fast applications by default This architectural foundation enables the development patterns we'll explore in the coming chapters. With this understanding, you're ready to start building with SartajPHP. --- *Next: Chapter 3 covers Installation and Project Setup, where we'll put these concepts into practice.* # **Chapter 3: Installation and Project Setup** ## **3.1 System Requirements and Prerequisites** Before installing SartajPHP, ensure your development environment meets these requirements: ### **3.1.1 Server Requirements** - **PHP 7.0 or higher** (7.0+ recommended) - **Composer** (for dependency management) - **Web Server** (SphpServer, Apache 2.4+, Nginx 1.18+, or built-in PHP server) - **Extensions Required**: - PDO (with MySQL, PostgreSQL, or SQLite drivers) - JSON - MBString - OpenSSL - Zip (for Composer operations) ### **3.1.2 Optional Components** - **Node.js 14+** (for SphpDesk and npm tools) - **Redis** (for advanced caching) - **SphpServer** (for NativeGate features) - **MySQL 5.7+ / MariaDB 10.3+ / PostgreSQL 12+** (for production databases) ### **3.1.3 Development Tools** - **Git** (version control) - **VS Code** (recommended IDE with SartajPHP extension) - **Composer** (PHP package manager) - **SphpDesk** (optional desktop environment) ## **3.2 Installation Methods** SartajPHP supports multiple installation methods to suit different workflows. ### **3.2.1 Composer Installation (Recommended)** For new projects, use Composer to create a SartajPHP project: ```bash # Create a new directory for your project mkdir my-sartaj-project cd my-sartaj-project # Initialize Composer and install SartajPHP composer init --name="mycompany/myproject" --type="project" --no-interaction composer require sartajphp/sartajphp # Install sample project structure composer run-script post-install-cmd ``` This creates the following structure: ``` my-sartaj-project/ ├── vendor/sartajphp/sartajphp/ # Framework core ├── apps/ # Your applications ├── masters/ # Master templates ├── cache/ # Cache directory ├── composer.json # Dependencies ├── start.php # Entry point └── .htaccess # URL rewriting ``` ### **3.2.2 Manual Installation** For environments without Composer or custom deployments: ```bash # Download the framework wget https://github.com/sartajphp/sartajphp/releases/latest/sartajphp.zip unzip sartajphp.zip -d /var/www/myproject # Set up project structure cd /var/www/myproject cp -r res/proj1/* . ``` ### **3.2.3 Docker Installation** For containerized development: ```dockerfile # Dockerfile FROM php:8.2-apache RUN apt-get update && apt-get install -y \ git unzip libzip-dev \ && docker-php-ext-install zip pdo_mysql RUN curl -sS https://getcomposer.org/installer | php -- \ --install-dir=/usr/local/bin --filename=composer WORKDIR /var/www/html COPY . . RUN composer install EXPOSE 80 ``` Create a `docker-compose.yml`: ```yaml version: '3.8' services: app: build: . ports: - "8080:80" volumes: - .:/var/www/html - ./cache:/var/www/html/cache environment: - APP_ENV=development db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: sartajdb ``` ## **3.3 Project Structure Explained** Understanding the project structure is crucial for effective development. ### **3.3.1 Core Project Structure** ``` myproject/ ├── .htaccess # Apache/Nginx rewrite rules ├── start.php # Application entry point ├── composer.json # Dependencies and scripts ├── comp.php # Project configuration ├── reg.php # Application registry ├── cachelist.php # Cache configuration ├── prerun.php # Pre-execution hooks ├── app.sphp # Desktop app configuration │ ├── apps/ # Web applications (.gate.php files) │ ├── Index.gate.php # Main application │ ├── uuadmin.gate.php # Admin application │ ├── uuapi.gate.php # API application │ └── fronts/ # FrontFiles for apps │ ├── index_main.front │ ├── admin_main.front │ └── api_main.front │ ├── appsn/ # NativeGate and ConsoleGate │ ├── uuchat.gate.php # WebSocket chat server │ ├── uuworker.gate.php # Background worker │ └── console/ # CLI applications │ └── uubackup.gate.php │ ├── masters/ # Master templates and assets │ ├── default/ # Default master set │ │ ├── master.php # Main layout │ │ ├── menu_guest.php # Guest menu │ │ └── menu_admin.php # Admin menu │ ├── db.php # Database schema │ └── sphpcodeblock.php # Custom code blocks │ ├── plugin/ # Project-specific plugins │ ├── myplugin/ │ │ ├── uuplugin.gate.php │ │ └── regapp.php │ └── config.json │ └── cache/ # Runtime cache ├── front/ # Compiled FrontFiles ├── component/ # Component cache ├── output/ # Page output cache └── logs/ # Error and access logs ``` ### **3.3.2 The `res/` Folder - Framework Resources** The `res/` folder contains shared framework resources. Its location depends on installation method: **Composer installation:** ``` vendor/sartajphp/sartajphp/res/ ``` **Manual installation:** ``` /path/to/framework/res/ ``` **Key subdirectories:** - `res/Score/` - Core framework engine - `res/Slib/` - Shared libraries and Components - `res/jslib/` - JavaScript libraries (jQuery, Bootstrap, etc.) - `res/components/` - Additional Components - `res/plugin/` - Framework plugins ## **3.4 Configuration Files** ### **3.4.1 `start.php` - The Entry Point** The `start.php` file is the single entry point for all requests. Never rename or remove this file. **Minimal `start.php`:** ```php execute(true); } ``` **Customizing paths for different environments:** ```php // Development environment if ($_SERVER['HTTP_HOST'] === 'localhost') { $sharedpath = "./vendor/sartajphp/sartajphp"; $respath = "./vendor/sartajphp/sartajphp/res"; } // Production environment elseif ($_SERVER['HTTP_HOST'] === 'example.com') { $sharedpath = "/usr/share/sartajphp"; $respath = "https://cdn.example.com/sartajphp/res"; } // Staging environment else { $sharedpath = "/opt/sartajphp"; $respath = "/shared/res"; } ``` ### **3.4.2 `.htaccess` - URL Rewriting** Apache configuration for clean URLs: ```apache # .htaccess - Apache Configuration Options +FollowSymLinks -Indexes DirectoryIndex start.php RewriteEngine On # Base directory adjustment (if project is in subfolder) RewriteBase /myproject/ # Route all .html/.htm requests to start.php RewriteCond %{REQUEST_URI} \.(html|htm)$ [NC] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ start.php [NC,L] # Prevent direct access to framework files RewriteCond %{REQUEST_URI} \.(front|app|sphp)$ [NC] RewriteRule ^(.*)$ start.php [NC,L] # Force HTTPS in production RewriteCond %{HTTPS} off RewriteCond %{HTTP_HOST} ^(www\.)?example\.com$ [NC] RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] # Cache static assets Header set Cache-Control "max-age=31536000, public" ``` **Nginx configuration equivalent:** ```nginx server { listen 80; server_name example.com; root /var/www/myproject; index start.php; # Clean URLs location ~ \.(html|htm)$ { try_files $uri $uri/ /start.php?$args; } # Protect framework files location ~ \.(front|app|sphp)$ { return 403; } # PHP processing location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } # Static file caching location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } } ``` ### **3.4.3 `comp.php` - Project Configuration** The `comp.php` file contains project-wide settings: ```php getAuthenticateType()) { case "ADMIN": $page->forward(getGateURL("admhome")); break; case "MEMBER": $page->forward(getGateURL("mebhome")); break; default: $page->forward(getGateURL("index")); break; } } ``` ### **3.4.4 `reg.php` - Application Registry** The `reg.php` file maps URLs to application files: ```php isRegisterCurrentRequest()) { $gate = SphpBase::sphp_router()->getCurrentRequest(); $appPath = PROJ_PATH . "/apps/" . ucfirst($gate) . ".gate.php"; if (is_file($appPath)) { SphpBase::sphp_router()->registerCurrentRequest($appPath); } } ``` ### **3.4.5 `prerun.php` - Pre-Execution Hooks** The `prerun.php` file runs before any application loads: ```php addProp('site_name', 'My Project'); SphpBase::sphp_api()->addProp('current_year', date('Y')); // Security headers $policy = SphpBase::sphp_response()->getSecurityPolicy( "https://*.googleapis.com https://*.gstatic.com https://*.cloudflare.com 'self'" ); SphpBase::sphp_response()->addSecurityHeaders($policy); // Additional security headers SphpBase::sphp_response()->addHttpHeader( 'X-Content-Type-Options', 'nosniff' ); SphpBase::sphp_response()->addHttpHeader( 'X-Frame-Options', 'SAMEORIGIN' ); SphpBase::sphp_response()->addHttpHeader( 'Referrer-Policy', 'strict-origin-when-cross-origin' ); // CORS headers for API if (strpos(SphpBase::sphp_request()->server('REQUEST_URI'), '/api/') !== false) { SphpBase::sphp_response()->addHttpHeader( 'Access-Control-Allow-Origin', 'https://app.example.com' ); SphpBase::sphp_response()->addHttpHeader( 'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS' ); SphpBase::sphp_response()->addHttpHeader( 'Access-Control-Allow-Headers', 'Content-Type, Authorization' ); } // Performance headers SphpBase::sphp_response()->addHttpHeader( 'Cache-Control', 'max-age=3600, public' ); // Load global libraries $this->loadGlobalLibraries(); } private function loadGlobalLibraries() { // Auto-load Composer dependencies if (file_exists(PROJ_PATH . '/vendor/autoload.php')) { require_once PROJ_PATH . '/vendor/autoload.php'; } // Load project-specific helpers if (file_exists(PROJ_PATH . '/helpers.php')) { require_once PROJ_PATH . '/helpers.php'; } } } ``` ### **3.4.6 `cachelist.php` - Cache Configuration** Configure caching for optimal performance: ```php connect(); // Create users table $sql = "CREATE TABLE IF NOT EXISTS users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, role ENUM('admin', 'member', 'guest') DEFAULT 'guest', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_email (email), INDEX idx_role (role) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"; $mysql->createTable($sql); // Create posts table $sql = "CREATE TABLE IF NOT EXISTS posts ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, slug VARCHAR(200) UNIQUE NOT NULL, status ENUM('draft', 'published', 'archived') DEFAULT 'draft', published_at TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_slug (slug), INDEX idx_status (status), INDEX idx_user (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"; $mysql->createTable($sql); // Insert default admin user (password: admin123) $mysql->executeQuery( "INSERT IGNORE INTO users (username, email, password_hash, role) VALUES ('admin', 'admin@example.com', '". password_hash('admin123', PASSWORD_DEFAULT) ."', 'admin')" ); $mysql->disconnect(); ``` Run the database initialization: ```bash # Via CLI php start.php --gate install --evt db # Or via browser # you may be need login as ADMIN # Access http://localhost/myproject/install-db.html ``` ## **3.6 Production Deployment** ### **3.6.1 Deployment Checklist** Before deploying to production: 1. **Update configuration:** ```php // In comp.php $debugmode = 0; // Disable debug mode ``` 2. **Set secure permissions:** ```bash chmod 755 /var/www/myproject chmod 644 /var/www/myproject/*.php chmod 777 /var/www/myproject/cache # Writeable for cache chown www-data:www-data /var/www/myproject -R ``` 3. **Enable HTTPS:** ```apache # .htaccess RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] ``` 4. **Configure production database:** ```php // In comp.php $dhost = "production-db.example.com"; $duser = "prod_user"; $dpass = "strong_password_here"; $db = "production_db"; ``` ### **3.6.2 Deployment Script** Create a deployment script (`deploy.sh`): ```bash #!/bin/bash # deploy.sh - Production deployment script set -e # Exit on error echo "Starting deployment..." # Pull latest code git pull origin main # Install dependencies composer install --no-dev --optimize-autoloader # Clear cache (in case of file changes) rm -rf cache/* # Set permissions chmod -R 755 . chmod -R 777 cache # Run database migrations if needed php start.php --gate migrate --evt run # Restart services sudo systemctl restart apache2 sudo systemctl restart sphp-server # If using NativeGate echo "Deployment complete!" ``` ### **3.6.3 Environment-Based Configuration** For different environments, use environment variables: ```php // Detect environment $env = getenv('APP_ENV') ?: 'development'; // Load environment-specific config switch ($env) { case 'production': $config = require 'config/production.php'; break; case 'staging': $config = require 'config/staging.php'; break; default: $config = require 'config/development.php'; break; } // Apply to comp.php settings $debugmode = $config['debug'] ? 2 : 0; $dhost = $config['database']['host']; // ... etc ``` ## **3.7 Troubleshooting Common Setup Issues** ### **3.7.1 "File Not Found" Errors** **Problem:** URLs return 404 errors even though files exist. **Solution:** 1. Check `.htaccess` file exists and is readable 2. Ensure Apache `mod_rewrite` is enabled: ```bash sudo a2enmod rewrite sudo systemctl restart apache2 ``` 3. Verify Apache configuration allows `.htaccess` overrides: ```apache AllowOverride All Require all granted ``` ### **3.7.2 Permission Denied for Cache** **Problem:** "Permission denied" errors when writing to cache directory. **Solution:** ```bash # Set correct ownership sudo chown www-data:www-data /path/to/project/cache -R sudo chmod 775 /path/to/project/cache # Or for development (less secure) chmod 777 /path/to/project/cache ``` ### **3.7.3 Database Connection Errors** **Problem:** Cannot connect to database. **Solution:** 1. Verify credentials in `comp.php` 2. Check database server is running 3. Ensure PDO extension is installed: ```bash php -m | grep pdo ``` 4. Test connection manually: ```php getMessage(); } ``` ### **3.7.4 Components Not Loading** **Problem:** Components with `runat="server"` don't work. **Solution:** 1. Check PHP version (requires 7.0+) 2. Verify `res/` path is correct in `start.php` 3. Check file permissions on Component classes 4. Clear cache: `rm -rf cache/*` ## **3.8 Chapter Summary** In this chapter, we've covered: 1. **System Requirements** - What you need to run SartajPHP 2. **Installation Methods** - Composer, manual, and Docker options 3. **Project Structure** - Understanding the directory layout 4. **Configuration Files** - Setting up `start.php`, `.htaccess`, `comp.php`, etc. 5. **Development Environment** - Tools and setup for efficient development 6. **Production Deployment** - Best practices for going live 7. **Troubleshooting** - Solving common setup issues With your environment properly configured, you're ready to start building applications. In the next chapter, we'll dive into the heart of SartajPHP: the Event-Oriented Paradigm and how to structure your applications around events rather than controllers. --- *Next: Chapter 4 explores the Event-Oriented Paradigm in depth, showing how to design applications around PageEvents rather than traditional MVC controllers.* # **Chapter 4: Understanding the Event-Oriented Paradigm** ## **4.1 The Paradigm Shift: From Routes to Events** SartajPHP framework don't use a **route-to-controller** model where URLs are mapped to controller methods. SartajPHP revolutionizes this with an **event-oriented** approach where URLs trigger **PageEvents** in applications. ### **4.1.1 The Traditional MVC Limitation** Traditional MVC. 1. **Routing complexity** grows with application size 2. **Controller bloat** as all user-related logic piles up 3. **URL fragility** when refactoring 4. **Limited reuse** of logic across different entry points ### **4.1.2 The SartajPHP uses Gate‑Driven Event Architecture (GDEA)** In SartajPHP, the simple user management: Browser send Request to SartajPHP Framework which translate URL into PageEvents and pass to your registered Gate file `User.gate.php` register inside `reg.php` file with registerGate function. Framework find and load your Gate Class and transfer request as events. Framework don't use autoload to load your Gate classes, so your Gate classes should be registered in `reg.php` file. If your Gate class don't handle Browser Requested Events then Browser received empty output but if Browser requested to non Registered Gate then receive error output page not found. ```php // apps/User.gate.php class User extends \Sphp\tools\BasicGate { public function page_new() { // List users (GET /user.html) $this->renderUserList(); } public function page_submit() { // Handle form submission (POST /user.html) $this->processUserForm(); } public function page_event_view($evtp) { // View user (GET /user-view-123.html) $this->showUserDetails($this->page->evtp); } public function page_event_search($evtp) { // Search users (GET /user-search-john.html) $this->searchUsers($evtp); } public function page_event_export($evtp) { // Export users (GET /user-export-csv.html) $this->exportUsers($evtp); } } ``` **Key advantages:** - **No routing configuration** - URLs map directly to methods - **Natural organization** - Events group related functionality - **Easy discovery** - See all supported actions in one class - **Flexible extension** - Add new events without configuration ## **4.2 PageEvents: The Core Abstraction** ### **4.2.1 Built-in PageEvents** SartajPHP recognizes several built-in PageEvents that handle common web patterns: | PageEvent | URL Pattern | HTTP Method | Typical Use | |-----------|-------------|-------------|-------------| | `page_new()` | `gate.html` | GET | Request to Landing URL | | `page_submit()` | `gate.html` | POST | Data Post to Landing URL | | `page_event_*()` | `gate-*-evtp.html` | GET/POST | Request to any Other URL of Gate | ### **4.2.2 The page_submit() Dispatcher** The `page_submit()` method is special—it acts as a dispatcher: ```php public function page_submit() { // 1. Validate all Components if (getCheckErr()) { // Validation failed, return to form $this->setFrontFile($this->frtMain); return; } /** 2. Determine if Form is insert or update * you can check isUpdateRequest of Sphp\Comp\Form\HTMLForm Component */ if ($this->frtMain->form2->isUpdateRequest()) { $this->updateMe(); } else { $this->insertMe(); } } ``` This code is same for Form Data handling in any Other Event Handler. ### **4.2.3 PageEvents for Other URL rather then Landing URL** Any method starting with `page_event_` becomes a PageEvent Handler and map URL: ```php // URL: blog-search-php.html public function page_event_search($evtp) { // $evtp = "php" $results = $this->searchPosts($evtp); $this->displayResults($results); } // URL: user-activate-abc123.html public function page_event_activate($evtp) { // $evtp = "abc123" $this->activateUser($evtp); $this->page->forward(getGateURL("user")); } // URL: shop-filter-price-asc-category-electronics.html public function page_event_filter($evtp) { // $evtp = "price-asc-category-electronics" $filters = explode('-', $evtp); // Process complex filter parameters } ``` ## **4.3 URL-to-Event Mapping Rules** Understanding how URLs map to PageEvents is crucial for effective SartajPHP development. ### **4.3.1 Basic Mapping Rules** ``` URL: --.html │ │ │ │ │ │ │ └── .html or .gate as file extension │ │ │ │ │ └── Event parameter (optional) │ │ │ └── Event name (or built-in event) │ └── Gate (registered application name) ``` **Examples:** - `index.html` → `Index.gate.php::page_new()` - `blog-view-123.html` → `Blog.gate.php::page_event_view($evtp)` with `$evtp = "123"` - `shop-category-electronics.html` → `Shop.gate.php::page_event_category("electronics")` - `admin-user-delete-456.html` → `Admin.gate.php::page_event_user("delete-456")` ### **4.3.2 Complex Parameter Handling** For multi-part parameters, use delimiter conventions: ```php // URL: products-filter-price-100-200-category-electronics.html public function page_event_filter($evtp) { // $evtp = "price-100-200-category-electronics" // Parse parameters $parts = explode('-', $evtp); $filters = []; $currentKey = null; foreach ($parts as $part) { if (in_array($part, ['price', 'category', 'brand', 'rating'])) { $currentKey = $part; $filters[$currentKey] = []; } elseif ($currentKey) { $filters[$currentKey][] = $part; } } // $filters = ['price' => ['100', '200'], 'category' => ['electronics']] $this->applyFilters($filters); } ``` ### **4.3.3 Alternative: Query Parameters** While SartajPHP prefers path-based parameters, query strings can be used for optional data: ```php // URL: search.html?q=keyword&page=2&sort=date public function page_new() { $query = $this->Client->request('q'); $page = (int)$this->Client->request('page', 1); $sort = $this->Client->request('sort', 'relevance'); $this->performSearch($query, $page, $sort); } ``` ## **4.4 Application Lifecycle in Detail** Understanding the complete lifecycle helps you place code in the right events. ### **4.4.1 Complete Lifecycle Sequence** ``` ┌─────────────────────────────────────────────────────────┐ │ Request Received │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Engine Initialization │ │ - Load framework core │ │ - Parse CLI arguments (if console) │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Router Analysis │ │ - Extract Gate from URL │ │ - Identify event type │ │ - Parse event parameters │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Application Loading │ │ - Check registration in reg.php │ │ - Load .gate.php file │ │ - Create Gate instance │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ prerun.php │ │ - Security headers │ │ - Global initialization │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onstart() │ │ - Gate-specific setup │ │ - FrontFile creation │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ FrontFile Parsing │ │ - Parse .front file │ │ - Create Components │ │ - Execute Parse Phase Fusion │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onfrontinit() │ │ - FrontFile initialized callback │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onfrontprocess() │ │ - FrontFile ready callback, after onaftercreate │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onready() │ │ - Gate ready to process │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onrun() │ │ - Pre-PageEvent processing │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ PageEvent Execution │ │ - page_new() or page_event_*() or page_submit() │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ FrontFile Execution │ │ - Execute Render Phase Fusion │ │ - Process runtime attributes │ │ - Generate HTML output │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onrender() │ │ - Final output modification │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ MasterFile Merge │ │ - Apply master template │ │ - Inject menus, headers, footers │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Cache Store │ │ - Check cacheability │ │ - Store if cacheable │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Response Sent │ └─────────────────────────────────────────────────────────┘ ``` ### **4.4.2 Lifecycle Event Purposes** **onstart()** - Setup phase ```php public function onstart() { // Check authentication, Should be First line // who has Application's Gate Permit, set by Signin.gate.php in session with // setSession('ADMIN', intval($authcookie['sid'])) $this->getAuthenticate("MEMBER,ADMIN"); // check permissions if you use permssion system $this->page->getAuthenticatePerm("view,add"); // Initialize FrontFiles $this->frtMain = new FrontFile($this->mypath . "/fronts/main.front"); // Load configuration $this->config = $this->loadConfig(); // Connect to external services $this->apiClient = new ApiClient(); } ``` **onready()** - Preparation phase ```php public function onready() { // All Components are created // FrontFile is parsed but not rendered // Good place for Component initialization $searchBox = $this->frtMain->getComponent("txtSearch"); $searchBox->fu_setDefaultValue($this->getLastSearch()); } ``` **onrun()** - Pre-processing phase ```php public function onrun() { // Executes before PageEvent // Good for authentication checks, logging if (!$this->hasPermission("view_page")) { // this will stop futher execution immediately with // call of SphpBase::engine()->exitMe(); $this->page->forward(getGateURL("unauthorized")); } $this->logAccess(); } ``` **onrender()** - Final modification phase ```php public function onrender() { // After FrontFile rendering, before MasterFile // You Can't modify Front File final output // but you change master file or add Javascript,CSS code if ($this->page->getAuthenticateType() != "ADMIN") { // Inject analytics code addHeaderJSCode("ga('send', 'pageview');"); } // or file links addFileLink("mytheme.css"); // or add Front Places for master addFrontPlace("banner",__DIR__ . "/banner2.front"); } ``` ## **4.5 Designing Event-Oriented Applications** ### **4.5.1 Single Responsibility Applications** Instead of monolithic controllers, create focused applications: ``` apps/ ├── User.gate.php # User management ├── Blog.gate.php # Blog system ├── Shop.gate.php # E-commerce ├── Cart.gate.php # Shopping cart ├── Checkout.gate.php # Checkout process ├── Admin.gate.php # Administration └── Api.gate.php # JSON API ``` Each app handles a cohesive set of related events. ### **4.5.2 Event Naming Conventions** Use consistent naming for discoverability: ```php // User management events public function page_event_login($evtp) {} // user-login.html public function page_event_register($evtp) {} // user-register.html public function page_event_profile($evtp) {} // user-profile-edit.html public function page_event_password($evtp) {} // user-password-reset.html // Content management events public function page_event_create($evtp) {} // blog-create.html public function page_event_edit($evtp) {} // blog-edit-123.html public function page_event_publish($evtp) {} // blog-publish-123.html public function page_event_archive($evtp) {} // blog-archive-123.html // E-commerce events public function page_event_add($evtp) {} // cart-add-456.html public function page_event_update($evtp) {} // cart-update-456.html public function page_event_remove($evtp) {} // cart-remove-456.html public function page_event_clear($evtp) {} // cart-clear.html ``` ### **4.5.3 Parameter Design Patterns** **Simple ID parameters:** ```php // URL: product-view-123.html public function page_event_view($evtp) { $productId = (int)$this->page->evtp; // Use $productId } ``` **Slug-based parameters:** ```php // URL: blog-post-my-awesome-article.html public function page_event_post($evtp) { $slug = $evtp; // "my-awesome-article" $post = $this->getPostBySlug($slug); } ``` **Action-object patterns:** ```php // URL: admin-user-activate-abc123.html public function page_event_user($evtp) { // $evtp = "activate-abc123" list($action, $userId) = explode('-', $evtp, 2); switch ($action) { case 'activate': $this->activateUser($userId); break; case 'deactivate': $this->deactivateUser($userId); break; case 'promote': $this->promoteUser($userId); break; } } ``` **Multi-parameter patterns:** ```php // URL: search-query-php-category-tutorials-sort-date.html public function page_event_search($evtp) { $params = []; $parts = explode('-', $evtp); for ($i = 0; $i < count($parts); $i += 2) { if (isset($parts[$i + 1])) { $params[$parts[$i]] = $parts[$i + 1]; } } // $params = ['query' => 'php', 'category' => 'tutorials', 'sort' => 'date'] $this->performSearch($params); } ``` ## **4.6 Advanced Event Patterns** ### **4.6.1 Event Chaining** Events can trigger other events: ```php public function page_event_process($evtp) { // Process data $result = $this->processData($evtp); if ($result->needsReview()) { // Chain to review event in another App $this->page->forward(getEventURL("review", $result->id,"gate")); } else { // Chain to complete event handler inside this Gate $this->page_event_complete($result->id); } } ``` ### **4.6.2 Event Middleware** Add pre/post processing to events: ```php public function page_event_download($evtp) { // Pre-processing $this->logDownloadAttempt(); if (!$this->validateDownloadAccess($evtp)) { return $this->page->forward(getGateURL("access-denied")); } // Main event logic $file = $this->getDownloadFile($evtp); $this->serveFile($file); // Post-processing $this->recordDownloadComplete(); } ``` ### **4.6.3 Event Broadcasting** NativeGates can broadcast events to multiple clients: ```php // In a NativeGate (chat server) public function page_event_message($evtp) { $message = $this->Client->post("message"); $sender = $this->Client->post("user"); // Broadcast to all connected clients $this->sendAll([ 'type' => 'message', 'sender' => $sender, 'message' => $message, 'timestamp' => time() ]); } ``` ## **4.7 Error Handling in Events** ### **4.7.1 Structured Error Responses** ```php public function page_event_api($evtp) { try { $data = $this->processApiRequest($evtp); $this->JSServer->addJSONReturnBlock([ 'success' => true, 'data' => $data, 'timestamp' => time() ]); } catch (ValidationException $e) { $this->JSServer->addJSONReturnBlock([ 'success' => false, 'error' => 'validation_failed', 'message' => $e->getMessage(), 'fields' => $e->getErrors() ]); } catch (AuthenticationException $e) { $this->JSServer->addJSONReturnBlock([ 'success' => false, 'error' => 'authentication_required', 'message' => 'Please log in to continue' ]); } catch (Exception $e) { // Log full error for debugging error_log("API error: " . $e->getMessage()); // Generic error for client $this->JSServer->addJSONReturnBlock([ 'success' => false, 'error' => 'server_error', 'message' => 'An unexpected error occurred' ]); } } ``` ### **4.7.2 User-Friendly Error Pages** ```php public function page_event_process($evtp) { if (!$this->validateInput($evtp)) { $this->setFrontFile($this->frtError); $this->frtError->getComponent("errorMessage") ->setInnerHTML("Invalid input provided"); /** * inform all Framework about error, * So Further processing can effect. Alert Component can * automatically display all user errors in Front File. just use * */ setErr("app","Invalid input provided"); return; } try { $this->processData($evtp); } catch (DatabaseException $e) { $this->setFrontFile($this->frtError); $this->frtError->getComponent("errorMessage") ->setInnerHTML("Database error. Please try again."); $this->debug->write_log("Database error:-" . $e->getMessage()); //send error message on console window of browser $this->debug->println("Database error:-" . $e->getMessage()); } } ``` ## **4.8 Testing Event-Oriented Applications** ### **4.8.1 Unit Testing Events** ```php // tests/UserAppTest.php class UserAppTest extends \PHPUnit\Framework\TestCase { public function testLoginEvent() { // Mock the App $app = $this->createMock(UserApp::class); // Test successful login $_POST['username'] = 'testuser'; $_POST['password'] = 'password123'; $app->expects($this->once()) ->method('authenticateUser') ->with('testuser', 'password123') ->willReturn(true); // Would need framework-specific testing setup } } ``` ### **4.8.2 Integration Testing URLs** ```php // tests/UrlMappingTest.php class UrlMappingTest extends \PHPUnit\Framework\TestCase { public function urlProvider() { return [ ['index.html', 'index', 'new', ''], ['blog-view-123.html', 'blog', 'view', '123'], ['shop-category-electronics.html', 'shop', 'category', 'electronics'], ]; } /** * @dataProvider urlProvider */ public function testUrlParsing($url, $ExpectedGate, $expectedEvent, $expectedParam) { $result = parseSartajUrl($url); $this->assertEquals($ExpectedGate, $result['gate']); $this->assertEquals($expectedEvent, $result['event']); $this->assertEquals($expectedParam, $result['parameter']); } } ``` ## **4.9 Performance Considerations** ### **4.9.1 Event-Specific Caching** ```php // In cachelist.php // Cache public events longer addCacheList("blog-view", 3600, "ce"); // 1 hour for blog views addCacheList("shop-product", 1800, "ce"); // 30 minutes for products // Cache personalized events shorter or not at all addCacheList("user-profile", 300, "ce"); // 5 minutes for profiles // No cache for cart, so no need any code // In the App public function page_event_expensive($evtp) { $cacheKey = "expensive_" . md5($evtp); // read database through cache $result = $this->dbEngine->fetchQuery("SELECT * FROM users WHERE status=1",300); $str1 = ""; foreach($result["news"] as $key=>$row){ $str1 .= '

'. $row["user_name"] .'

'; } $divuser = $this->frtMain->getComponent("divuser"); $divuser->setInnerHTML($str1); $divuser->setAttribute("class","card-body"); $divuser->setPreTag('

'); $divuser->setPostTag('
'); if ($cached = SphpBase::sphp_api()->readFromCache($cacheKey,300)) { // 5 minutes $this->frtMain->div1->setInnerHTML($cached); return; } $result = $this->expensiveOperation($evtp); SphpBase::sphp_api()->saveToCache($cacheKey, $result); $this->frtMain->div1->setInnerHTML($result); } ``` ### **4.9.2 Lazy Event Initialization** ```php public function onstart() { // Don't load heavy resources unless needed if ($this->page->getEvent() === 'report') { $this->reportGenerator = new HeavyReportGenerator(); $this->chartLibrary = new ChartingLibrary(); } } public function page_event_report($evtp) { // Resources already loaded in onstart() $report = $this->reportGenerator->generate($evtp); $charts = $this->chartLibrary->render($report); $this->displayReport($report, $charts); } ``` ## **4.10 Real-World Example: Blog System** Let's build a complete blog system using event-oriented design: ```php // apps/Blog.gate.php class Blog extends \Sphp\tools\BasicGate { private $frtMain; private $frtPost; private $frtAdmin; public function onstart() { // GUEST (No login), MEMBER and ADMIN can access this App $this->getAuthenticate("GUEST,MEMBER,ADMIN"); // only few events are secure for ADMIN, So loose security type App $this->frtMain = new FrontFile($this->mypath . "/fronts/blog_main.front"); $this->frtPost = new FrontFile($this->mypath . "/fronts/blog_post.front"); $this->frtAdmin = new FrontFile($this->mypath . "/fronts/blog_admin.front"); } // Public events public function page_new() { // List blog posts (GET /blog.html) $posts = $this->getRecentPosts(10); $this->frtMain->getComponent("postList")->setInnerHTML($posts); $this->setFrontFile($this->frtMain); } public function page_event_view($evtp) { // View single post (GET /blog-view-123.html) $postId = (int)$this->page->getEventParameter(); $post = $this->getPost($postId); if (!$post) { $this->page->forward(getGateURL("not-found")); return; } $this->frtPost->getComponent("postTitle")->setInnerHTML($post->title); $this->frtPost->getComponent("postContent")->setInnerHTML($post->content); $this->setFrontFile($this->frtPost); // Increment view count $this->incrementViews($postId); } public function page_event_category($evtp) { // Posts by category (GET /blog-category-php.html) $posts = $this->getPostsByCategory($evtp); $this->frtMain->getComponent("postList")->setInnerHTML($posts); $this->frtMain->getComponent("pageTitle") ->setInnerHTML("Posts in category: " . $evtp); $this->setFrontFile($this->frtMain); } public function page_event_search($evtp) { // Search posts (GET /blog-search-keywords.html) $posts = $this->searchPosts($evtp); $this->frtMain->getComponent("postList")->setInnerHTML($posts); $this->frtMain->getComponent("pageTitle") ->setInnerHTML("Search results for: " . $evtp); $this->setFrontFile($this->frtMain); } // Admin events (protected) public function page_event_admin($evtp) { // Admin dashboard (GET /blog-admin.html) if (!$this->page->getAuthenticateType() == "ADMIN") { $this->page->forward(getGateURL("login")); return; } $stats = $this->getBlogStats(); $this->frtAdmin->getComponent("stats")->setInnerHTML($stats); $this->setFrontFile($this->frtAdmin); } public function page_event_create($evtp) { // Create post form (GET /blog-create.html) $this->requireAdmin(); $this->setFrontFile($this->frtAdmin); } public function page_submit() { // Handle post creation/update (POST /blog.html) $this->requireAdmin(); if (getCheckErr()) { // Validation failed $this->setFrontFile($this->frtAdmin); return; } if ($this->page->isInsertMode()) { $postId = $this->createPost(); $this->page->forward(getEventURL("edit", $postId)); } else { $this->updatePost(); $this->page->forward(getEventURL("admin")); } } public function page_event_publish($evtp) { // Publish post (GET /blog-publish-123.html) $this->requireAdmin(); $this->publishPost((int)$evtp); $this->page->forward(getEventURL("admin")); } // Helper methods private function requireAdmin() { if (!$this->page->getAuthenticateType() == "ADMIN") { $this->page->forward(getGateURL("login")); } } private function getRecentPosts($limit) { $db = SphpBase::dbEngine(); return $db->executeQuery( "SELECT * FROM posts WHERE status = 'published' ORDER BY published_at DESC LIMIT $limit"); } // ... other helper methods } ``` ## **4.11 Chapter Summary** The event-oriented paradigm in SartajPHP represents a fundamental shift in how we think about web application architecture: 1. **Natural Mapping**: URLs directly correspond to application events 2. **Reduced Boilerplate**: No routing configuration needed 3. **Better Organization**: Related functionality grouped in event methods 4. **Flexible Extension**: New features added as new events 5. **Clear Lifecycle**: Well-defined execution order Key takeaways: - Use `page_new()` for initial page loads - Use `page_submit()` for form handling (auto-dispatches to insert/update) - Use `page_event_*()` for custom functionality - Design URLs that make sense for users and developers - Group related events in focused applications This paradigm enables building maintainable, scalable applications that grow gracefully with your needs. In the next chapter, we'll explore the FrontFile system and how to create dynamic, reusable UI components. --- *Next: Chapter 5 dives into the FrontFile system, showing how to create pure HTML templates enhanced with server-side capabilities.* # **Chapter 5: The FrontFile System - HTML with Superpowers** ## **5.1 The Philosophy of Pure HTML Templates** SartajPHP's FrontFile system represents a radical departure from traditional PHP templating engines. Instead of inventing a new template language or forcing developers to learn special syntax, SartajPHP embraces **100% valid HTML** as its template foundation. ### **5.1.1 The Problem with Traditional Templating** Consider these popular templating approaches: **PHP Mixed with HTML (WordPress-style):** ```php

title); ?>

content; ?>
isAdmin()): ?> Edit
``` **Custom Template Languages (Blade/Laravel):** ```blade

{{ $post->title }}

{!! $post->content !!}
@can('edit', $post) Edit @endcan
``` **Both approaches have issues:** - **PHP-in-HTML** mixes logic with presentation, creating security risks - **Custom languages** require learning new syntax and break HTML validation - **Both** make collaboration with designers difficult - **Both** create vendor lock-in ### **5.1.2 The SartajPHP Solution: HTML-First Design** SartajPHP FrontFiles are **plain HTML files** that can be opened in any browser or editor. SartajPHP divide designing task into Front File + Master File + Front Places. Master File hold header and footer which design is same for whole project. The other SartajPHP Generated Code is injected through Front Places. like Front File output show with `SphpBase::getAppOutput()` . So we can't use html and body tag in Front File. Here is Sample Front File Code: ```html

Blog Post Title

This is the post content.

``` This HTML file can be: - **Designed** by UI/UX designers using standard tools - **Prototyped** and tested in browsers without PHP - **Version controlled** as plain text - **Validated** with HTML validators - **Processed** by build tools (minifiers, preprocessors) **Only when needed**, developers add special attributes to enable server-side behavior: ```html ``` ## **5.2 FrontFile Anatomy and Structure** ### **5.2.1 File Naming and Location Conventions** SartajPHP follows consistent naming patterns: ``` apps/ # Application directory ├── uumyapp.gate.php # Application class └── fronts/ # FrontFiles directory ├── myapp_main.front # Main layout (convention: appname_main.front) ├── myapp_detail.front # Detail view ├── myapp_form.front # Form view └── myapp_admin.front # Admin interface ``` **Best Practices:** - Use descriptive names: `user_profile.front`, `product_list.front`, `checkout_summary.front` - Group related files: `admin_users.front`, `admin_products.front`, `admin_settings.front` - Keep FrontFiles small and focused (single responsibility) ### **5.2.2 The Three Processing Phases** When a FrontFile is loaded, it goes through three distinct phases: #### **Phase 1: Parsing** The raw HTML is parsed into a DOM-like structure: ``` HTML Text → Parser → NodeTag Tree ``` Each HTML element becomes a `NodeTag` object with properties: - `tagName` (div, span, input, etc.) - `attributes` (class, id, style, etc.) - `parentNode` (parent element) - `childNodes` (array of child elements) #### **Phase 2: Component Creation** Tags with `runat="server"` are detected and corresponding Component objects are created: ```html NodeTag { tagName: "input", attributes: {type: "text", id: "username", runat: "server"}, component: TextFieldComponent {id: "username"} } ``` #### **Phase 3: Attribute Processing** Special attributes are processed according to their type: - `runas` attributes transform rendering behavior, can't use with `runat` - `runcb` attributes execute code blocks - `*` All attributes can use as data for Components - `fun-*` Fusion attributes can call methods of Components and pass data ",|" as separator - Expression tags (`##{ }#`) are evaluated but not all PHP syntax supported ### **5.2.3 Minimal FrontFile Example** We don't need html and body tags and jQuery and bootstrap because that all are provided by Master File and project wide. But other custom css and js that is only need for this Front File we can add in Front File. Here's a complete, working FrontFile: ```html Welcome to Our Site

##{$siteName}#

Admin Controls

Dashboard
``` ## **5.3 Runtime Attributes: Controlling Behavior** Runtime attributes are the key to SartajPHP's power. They transform ordinary HTML tags into dynamic, server-aware elements. ### **5.3.1 The Three Runtime Attributes** | Attribute | Purpose | Creates Component? | |-----------|---------|-------------------| | `runat="server"` | Makes tag a server-side Component | Yes | | `runas="value"` | Changes rendering behavior | No | | `runcb="true"` | Executes code blocks | No | ### **5.3.2 runat="server" - Server-Side Components** The `runat="server"` attribute is the most powerful runtime attribute. It tells SartajPHP: "Treat this HTML element as a server-side Component." SartajPHP identify Tag and find relative inbuilt Component or otherwise it will choose Tag Component or you can give path of your own Component File with `path` attribute. **Basic Usage:** ```html Loading...
``` **What happens when you add `runat="server"`:** 1. A Component object is instantiated 2. The Component binds to the NodeTag 3. Fusion attributes become active 4. The Component participates in lifecycle events 5. Server-side validation becomes possible 6. Database binding can be configured ### **5.3.3 runas - Rendering Transformations** The `runas` attribute changes how a tag is rendered without creating a Component. **Common `runas` values:** | Value | Purpose | Example | |-------|---------|---------| | `filelink` | Manage CSS/JS resources | `` | | `holder` | Dynamic content placeholder | `
` | | `jsfunction` | Generate JavaScript functions | ` ``` **When to use `renderonce`:** - ✅ Global JavaScript libraries (jQuery, Bootstrap) - ✅ Framework initialization code - ✅ CSS files that don't change - ✅ One-time analytics scripts **When NOT to use `renderonce`:** - ❌ Dynamic content that changes per request - ❌ User-specific JavaScript - ❌ AJAX response content - ❌ Real-time updates ## **5.5 Expression Tags: Dynamic Content in HTML** Expression tags provide a safe way to embed dynamic content without PHP. ### **5.5.1 Expression Tag Syntax** ```html

Welcome, ##{$userName}#!

#{ $pageTitle = "Home Page"; }#
##{raw:$htmlContent}#
``` ### **5.5.2 Available Variables in Expression Tags** Expression tags have access to a limited scope: ```html

##{$parentgate->siteName}#

##{$frontobj->getFilePath()}#

##{$sphp_settings->getBase_url()}#

##{$metadata->get('description')}#

``` ### **5.5.3 Safe Expression Patterns** **Good patterns:** ```html

##{$pageTitle}#

Products
Message
#{ $counter = 0; }#
##{foreach:$items as $item}#
##{$item->name}#
##{endforeach}#
``` **Avoid complex logic in expressions:** ```html #{ $result = []; foreach ($data as $item) { if ($item->active) { $result[] = processItem($item); } } $output = implode(', ', $result); }#

##{$output}#

``` ## **5.6 Special Tags and Their Behavior** ### **5.6.1 `` Tag Handling** The `<title>` tag is special in SartajPHP: ```html <!-- Regular HTML title --> <title>My Page Default Title public function page_new() { $this->frtMain->getComponent("pageTitle") ->fu_setValue("Welcome to " . $this->siteName); } ``` ### **5.6.2 `` and ` ``` ### **5.6.3 `
` Tag Enhancements** Forms gain superpowers with `runat="server"`: ```html
``` ## **5.7 Building Reusable Layouts** ### **5.7.1 Master-Detail Pattern** **Master layout (`masters/default/master.php`):** ```php getHeaderHTML(); ?>

© My Company

getFooterHTML(); // print all errors for debug purposes echo SphpBase::sphp_api()->traceError(true) . SphpBase::sphp_api()->traceErrorInner(true); ?> ``` **Content FrontFile (`apps/fronts/page_main.front`):** ```html

##{$pageTitle}#

``` ### **5.7.2 Component-Based Layouts** Build layouts from reusable Components: ```html
``` ### **5.7.3 Template Inheritance with Includes** Use the Include Component for template composition: ```html
``` ## **5.8 Practical FrontFile Examples** ### **5.8.1 User Registration Form** ```html

Create Account

Already have an account? Sign In
``` ### **5.8.2 Product Listing Page** ```html

Our Products

##{$productCount}# amazing products

##{$productGrid->getRow('name')}#
##{$productGrid->getRow('name')}#

##{$productGrid->getRow('description')}#

$##{$productGrid->getRow('price')}#
``` ### **5.8.3 Admin Dashboard** ```html

Dashboard

Total Users

##{$stats['userCount']}#

Total Orders

##{$stats['orderCount']}#

Revenue

$##{$stats['revenue']}#

Active Products

##{$stats['productCount']}#

Recent Activity

Time User Action Details
##{$div1->getItem('time')}# ##{$div1->getItem('user')}# ##{$div1->getItem('action')}# ##{$div1->getItem('details')}#
``` ## **5.9 Best Practices for FrontFile Development** ### **5.9.1 Organization Guidelines** 1. **Keep FrontFiles focused**: One FrontFile per "view" or "screen" 2. **Use consistent naming**: `entity_action.front` (e.g., `user_edit.front`, `product_list.front`) 3. **Group related files**: All admin FrontFiles in `fronts/admin/` directory 4. **Separate layout from content**: Use master templates for common structure ### **5.9.2 Performance Optimization** ```html #{ $result = complexCalculation(); foreach ($data as $item) { // more processing } }#
##{$result}#
``` ### **5.9.3 Security Considerations** ```html
##{$userContent}#
##{raw:$trustedHtml}#
##{$parentgate->safeParam}#
``` ### **5.9.4 Maintainability Tips** 1. **Comment complex sections**: Use HTML comments for documentation 2. **Use semantic class names**: `.user-profile-card` not `.div3` 3. **Keep expression tags simple**: Move complex logic to Gate layer 4. **Test FrontFiles standalone**: They should render as valid HTML without PHP ## **5.10 Common Pitfalls and Solutions** ### **5.10.1 "My runat='server' Component Isn't Working"** **Problem:** Component doesn't behave as expected. **Solution checklist:** 1. Verify `id` attribute is present and unique 2. Check that Component class exists and is loadable 3. Ensure no JavaScript errors are interfering 4. Clear cache: `rm -rf cache/*` 5. Check browser console for framework errors ### **5.10.2 "Expression Tags Not Evaluating"** **Problem:** `##{ }#` tags show as literal text. **Solutions:** 1. Ensure you're using `##{ }#` not `{{ }}` or other syntax 2. Check that variable exists in expression scope and in evaluation priority sequence 3. Verify no syntax errors in expression 4. Make sure FrontFile has `.front` extension ### **5.10.3 "CSS/JS Files Not Loading"** **Problem:** Resources with `runas="filelink"` don't appear. **Solutions:** 1. Check paths are correct (use `slibrespath/` for framework resources) 2. Ensure files exist at specified location 3. Verify no permission issues 4. Check browser network tab for 404 errors ## **5.11 Chapter Summary** The FrontFile system is SartajPHP's secret weapon for productive, maintainable web development: 1. **Pure HTML Foundation**: Start with valid HTML, enhance as needed 2. **Runtime Attributes**: Add server-side behavior with `runat`, `runas`, `runcb` 3. **Safe Expression Tags**: Embed dynamic content without security risks 4. **Component Integration**: Seamlessly connect HTML with server logic 5. **Framework Management**: Automatic resource loading, caching, optimization Key principles to remember: - FrontFiles are **designer-friendly** - they work as pure HTML - Runtime attributes are **optional** - add them only when needed - Expression tags are **restricted** - for safety and clarity - The framework **manages complexity** - focus on your application logic With FrontFiles, you get the best of both worlds: the simplicity of plain HTML with the power of a full-stack framework. In the next chapter, we'll dive deeper into Components - the server-side building blocks that bring FrontFiles to life. --- *Next: Chapter 6 explores Components in depth - how to create, configure, and leverage these powerful server-side UI building blocks.* # **Chapter 6: Components - Server-Side UI Building Blocks** ## **6.1 The Component Philosophy: Beyond Widgets** In SartajPHP, Components are not mere UI widgets—they are **complete server-side objects** that bridge the gap between HTML presentation and PHP business logic. Unlike traditional frameworks where form elements are passive data containers, SartajPHP Components are active participants in the application lifecycle with their own state, behavior, and intelligence. ### **6.1.1 What Makes a Component Special?** Consider a standard HTML input: ```html ``` This element is: - **Passive**: Just displays a value - **Dumb**: No validation logic - **Insecure**: Raw data exposure - **Isolated**: No connection to server logic Now see the same input as a SartajPHP Component: ```html ``` This Component is: - **Active**: Participates in validation - **Intelligent**: Knows its constraints - **Secure**: Auto-sanitizes data - **Connected**: Binds to database - **Lifecycle-aware**: Has initialization events ### **6.1.2 The Component-NodeTag Partnership** Every Component consists of two inseparable parts: 1. **Component Object** (PHP class) - Server-side logic and state - Validation rules and business logic - Database interaction methods - Lifecycle event handlers 2. **NodeTag Object** (HTML representation) - HTML element properties - DOM structure and relationships - Rendering control flags - Client-side attributes These two objects work together through a well-defined interface: ``` Component (PHP) ↔ NodeTag (HTML) │ │ ├── Sets values ───┤ ├── Controls rendering └── Handles events ``` ## **6.2 Component Types and Hierarchy** SartajPHP provides a comprehensive hierarchy of Components for different purposes. ### **6.2.1 Built-in Component Classes** ``` Sphp\tools\Component (Base Class) ├── Sphp\Comp\Form\HTMLForm (Forms) ├── Sphp\Comp\Form\TextField (Text inputs) │ ├── EmailField (Email inputs) │ ├── PasswordField (Password inputs) │ └── HiddenField (Hidden inputs) ├── Sphp\Comp\Form\TextArea (Textareas) ├── Sphp\Comp\Form\Select (Dropdowns) ├── Sphp\Comp\Form\CheckBox (Checkboxes) ├── Sphp\Comp\Form\Radio (Radio buttons) ├── Sphp\Comp\Form\DateField (Date pickers) ├── Sphp\Comp\Form\FileUploader (File uploads) ├── Sphp\Comp\Html\Alert (Alert/Message displays) ├── Sphp\Comp\Html\Img (Images) ├── Sphp\Comp\Html\Title (Page titles) ├── Sphp\Comp\Server\IncludeFront (Include other FrontFiles) ├── Sphp\Comp\Server\IncludePlace (Include placeholders) ├── \Pagination (Data pagination) ├── \DataGrid (Data tables) ├── Sphp\Comp\Server\ForLoop (Loop structures) └── Sphp\Comp\Tag (Generic tag handler) ``` ### **6.2.2 Component Categories** **Form Components** (User input): ```html ``` **Display Components** (Output): ```html Default ``` **Container Components** (Layout): ```html
``` **Data Components** (Collections): ```html
##{$dataGrid->getRow('id')}#
display here page links
##{'Counter:- ' . $forLoop->counter}#
##{'Key Item:- ' . $foreach->key . ' : ' . $foreach->getItem()}#
``` ### **6.2.3 Component Validations** #### Form Binding * `fui-setForm="form_id"` binds Controls to a Form * Enables automatic client-side validation * Errors can be displayed using `` Controls #### Example: Form with Alert frtMain Front File Object ```html
``` #### Server-side Handling (BasicGate) ```php public function page_submit(){ if(!getCheckErr()){ echo "Form Submitted Successfully
"; echo "Name: " . $this->Client->post("txtname"); // OR echo "OR Name: " . $this->frtMain->getComponent("txtname")->getValue(); } else { setErr("form1_err","Form Submission Failed"); $this->setTempFile($this->frtMain); } } ``` #### Form Submission Patterns ##### Standard HTTP POST * Uses `page_submit` * Validation handled automatically via `fuisetForm` ##### AJAX Submission * Add `fun-setAJAX=""` to `
` * Use `page_event_*` * Response sent via `JSServer` ```php public function page_submit(){ if(!getCheckErr()){ $this->JSServer ->addJSONHTMLBlock("form1_err","Form Submitted Successfully"); } else { $this->JSServer ->addJSONHTMLBlock("form1_err","Form Submission Failed"); } } ``` --- ##### WebSocket Form Submission (NativeGate) * `fun-setSocket="app,event,param"` binds the Form to a **NativeGate** * Not supported in BasicGate * Browser communicates via WebSocket ```html ...
``` **Server-side:** * `onwscon` * `onwsdiscon` * `page_event_*` --- ### Form Controls Overview SartajPHP provides multiple form controls: * `TextField` → text, email, password, numeric * `Textarea` → multi-line text input * `Select` → dropdowns with options * `Radio` → radio button groups * `Checkbox` → single/multi-option * `File` → file upload with validations * `Date` → date picker with min/max * `Alert` → display error messages Controls can handle both client-side and server-side validation. --- ### TextField and Textarea **Attributes:** * `placeholder` → used in validation messages * `fui-setForm` → bind to form for client-side validation * `fui-setRequired`, `fui-setMinLen`, `fui-setMaxLen` **Example – TextField** ```html ``` **Example – Textarea** ```html ``` --- ### File Control **Attributes:** * `fui-setFileTypesAllowed` → allowed MIME types * `fui-setFileSavePath` → save path * `fui-setFileMaxLen` / `fui-setFileMinLen` → file size limits **Example – File Upload** ```html ``` --- ### Radio and Checkbox **Radio Button Group** ```html ``` **Checkbox** ```html ``` --- ### Date Control **Attributes:** * `fui-setDateMin` / `fui-setDateMax` → date range * `fui-setNumMonths` → months visible in calendar * `fui-setAppendText` → display format **Example – Correct Date Control** ```html ``` --- ### Select Control **Options Binding:** * `fun-setOptionsJSON` → JSON string `[["1","Clothes"],["2","Electronics"]]` * `fun-setOptions` → comma-separated list * `fu_setOptionsArray` / `fu_setOptionsFromTable` → server-side **Example – AJAX-dependent Select** ```html ``` **Server-side – populate dependent Select** ```php public function page_event_sltcountry_change($evtp){ $lst = "state1,state2"; switch($this->frtMain->getComponent('sltcountry')->getValue()){ case "Country2": $lst = "2state1"; break; case "Country3": $lst = "3state1"; break; } $this->frtMain->getComponent('sltstate')->setOptions($lst); $this->JSServer->addJSONCompChildren($this->frtMain->getComponent('sltstate'),"sltstate"); } ``` --- ### Dynamic Control Validation * `placeholder` used as label in validation * `setMsgName` alternative for custom labels * `fui-setForm` attribute required for client-side form validation * Components Tag without `fui-setForm` attribute can still handle server-side validation --- ## **6.3 Component Lifecycle and Events** Understanding the Component lifecycle is crucial for effective development. ### **6.3.1 Complete Lifecycle Sequence** ``` 1. FrontFile Parsing ├── HTML parsed into NodeTags ├── runat="server" tags detected ├── Component objects instantiated └── NodeTag bound to Component ($this->element) 2. Parse Phase Initialization ├── Component::oninit() event ├── App::comp_*_on_init() hook (if on-init="true") ├── Fusion attributes with fui- prefix executed └── Component::oncreate() event 3. Request Processing ├── Component values populated from request ├── Validation executed (if form submitted) ├── Gate logic interacts with Components └── Component state updated 4. Render Phase ├── Component::onprerender() event ├── Fusion attributes with fur- prefix executed ├── Component::onrender() event ├── Component generates HTML output └── Component::onpostrender() event 5. Cleanup └── Component resources released ``` ### **6.3.2 Lifecycle Events in Detail** **Component Internal Events:** ```php class MyComponent extends \Sphp\tools\Component { // Called during initialization public function oninit() { // Setup default values // Initialize resources } // Called after Component is fully created public function oncreate() { // All attributes are parsed // Fusion methods have been called } // Called before rendering starts public function onprerender() { // Last chance to modify state before output } // Called during HTML generation public function onrender() { // Generate Component-specific HTML // Can modify NodeTag output } // Called after rendering completes public function onpostrender() { // Cleanup temporary resources // Finalize output } } ``` **Gate Hook Events (from FrontFile):** ```html ``` ```php // In Gate class public function comp_txtUser_on_init($evtp) { // $evtp contains event data $component = $evtp['obj']; // Component instance $element = $evtp['element']; // NodeTag instance // Modify Component before rendering $component->fu_setDefaultValue("Guest"); } public function comp_txtUser_on_create($evtp) { // Component fully created // All Fusion attributes processed } ``` ### **6.3.3 Lifecycle Timing Diagram** ``` Time ───────────────────────────────────────► │ │ FrontFile │ Parse HTML │ Parsing │ Detect runat="server" │ │ Create Components │ │ Bind NodeTags │ ├───────────────────────────────────────┤ Parse │ Component::oninit() │ Phase │ App::comp_*_on_init() │ │ fui-* Fusion execution │ │ Component::oncreate() │ │ App::comp_*_on_create() │ ├───────────────────────────────────────┤ Request │ Populate values from $_POST/$_GET │ Processing│ Execute validation │ │ Gate PageEvent logic │ ├───────────────────────────────────────┤ Render │ Component::onprerender() │ Phase │ fur-* Fusion execution │ │ Component::onrender() │ │ Generate HTML output │ │ Component::onpostrender() │ └───────────────────────────────────────┘ ``` ## **6.4 Component Properties and Methods** ### **6.4.1 Core Properties** Every Component has these essential properties: ```php // Component identity $this->id; // "txtUsername" (from id attribute of NodeTag) $this->name; // Same as id, used for name of Object $this->tagName // use for HTML Tag Node name $this->HTMLName // use for HTML Tag's name attribute $this->HTMLID // use for HTML Tag's id attribute $this->element; // Bound NodeTag object $this->parentobj // Parent Component Object $this->frontobj // parent Front File Object $this->mypath // Component Directory Path $this->myrespath // Component Directory URL // State properties $this->value; // Current value (posted or default) $this->defvalue; // Initial value $this->issubmit; // Browser Post this Component // Database properties (if bound) $this->dtable; // Database table name $this->dfield; // Database field name // Rendering properties $this->styler // Integer number Change output Layout design $this->renderMe; // Whether Component render or not $this->renderTag; // Whether Component Tag render or not $this->visible; // Whether Component hidden or visible ``` ### **6.4.2 Common Methods** **Value Management:** ```php // Get/set value $value = $component->getValue(); $component->value = "New Value"; // not safe, can over write Browser Post value $component->fu_setValue("Via Fusion"); // Safe, ignore if Browser Post Value // Default values $component->fi_setDefaultValue("Via fui-*"); // set Initial value // Value transformation not all component supported $component->getSqlSafeValue(); // Manually escaped value for database ``` **Validation Methods:** ```php // Check validation if (!getCheckErr()) { // Valid data } // Get validation errors $errors = getErrMsg(""); // Validation rules (called via fui-* attributes) $component->fi_setRequired(); // Field is required $component->fi_setEmail(); // Must be valid email $component->fi_setNumeric(); // Must be numeric $component->fi_setMinLen(3); // Minimum length $component->fi_setMaxLen(50); // Maximum length $component->fi_setMatch("otherField"); // Match another field ``` **Rendering Control:** ```php // Show/hide Component $component->setVisible(); // Make visible $component->unsetVisible(); // Make invisible $component->fu_setrender(); // Make render $component->fu_unsetrender(); // Make no output // HTML manipulation $component->setInnerHTML("Content"); $component->setOuterHTML("
Replacement
"); $component->setPreTag("Before"); $component->setPostTag("After"); // HTMLTag Manipulation through element property // set pre component tag html $component->setPreTag('Info Page
'); // set post component tag html $component->setPostTag('
'); // set html before children of component tag $component->element->setInnerPreTag('
Title
'); // set html after children of component tag $component->element->setInnerPostTag('
Info End
'); // set Inner html and replace all children of component tag $component->setInnerHTML('
Info End
'); // append Inner html of component tag $component->appendHTML('
Info End
'); // wrap component tag as child $component->appendHTML('
'); // wrap Inner html of component tag $component->appendHTML('
'); // Class and style through attributes // set attribute of component tag $component->setAttribute("style","color: red;"); // remove attribute of component tag $component->removeAttribute('class'); // check exist attribute of component tag $component->element->hasAttribute('class'); // set Initial attribute value of component tag $component->element->setDefaultAttribute('class','card'); // append attribute value of component tag $component->element->appendAttribute('class','btn-primary'); ``` **Database Operations:** ```php /** Database Bind Operations, Take Care by * SphpBase::page()->insert_data, update_data and view_data methods */ // Manually bind $component->bindToTable($table,$field); $component->bindToTable("users", "username"); /** Forcefully bind with Database from take values in Front File * Only available if dtable/dfield set */ $component->setDataBound(); // remove Database binding $component->unsetDataBound(); // get Database Binding Status, true or false $component->hasDatabaseBinding(); // Stop Insert to Database according to Permissions $component->fi_setDontInsert("GUEST,MEMBER"); // Stop Update to Database according to Permissions $component->fi_setDontUpdate("GUEST,MEMBER"); // In App, with page helper $this->page->viewData($component); // Auto-populate all Children from DB $this->page->insertData($component); // Insert All Children Component values $this->page->updateData($component); // Update All Children Component values ``` ## **6.5 Fusion Attributes: Component Control from FrontFile** Fusion attributes are the bridge between FrontFile markup and Component methods. ### **6.5.1 Fusion Prefixes and Timing** | Prefix | Execution Phase | Purpose | |--------|----------------|---------| | `fui-` | Parse Phase | Initial setup, validation rules | | `fun-` | Auto-decided | fi_=Parse, fu_=Render | | `fur-` | Render Phase | Output generation, dynamic values | **Parse Phase (`fui-`)** - Runs once during initialization: ```html ``` **Render Phase (`fur-`)** - Runs every render cycle: ```html ``` **Auto (`fun-`)** - Framework decides based on type of method prefix fi_ or fu_: ```html ``` ### **6.5.2 Common Fusion Methods** **TextField Component (`Sphp\Comp\Form\TextField`):** ```html fui-setRequired="" fui-setEmail="" fui-setNumeric="" fui-setMinLen="3" fui-setMaxLen="50" fui-setMatch="txtConfirm" placeholder="Email Address" fui-setForm="myForm" fui-setDefaultValue="example@test.com" fur-setValue="##{$userEmail}#" on-init="true" on-create="true"> ``` **Select Component (`Sphp\Comp\Form\Select`):** ```html ``` **CheckBox Component (`Sphp\Comp\Form\CheckBox`):** ```html ``` ### **6.5.3 The Special `_` Fusion Method** The `_` method is a generic setter for any attribute: ```html
``` This is equivalent to: ```php $component->element->setAttribute("class", $value); $component->element->setAttribute("style", $value); $component->element->setAttribute("data-user-id", $value); ``` ## **6.6 Database Binding and Operations** One of Component's most powerful features is automatic database integration. ### **6.6.1 Basic Database Binding** ```html ``` ### **6.6.2 Automatic CRUD Operations** With database-bound Components, CRUD becomes trivial: **In Gate class:** ```php // Create (Insert) public function page_submit() { // Components automatically read posted values // Values automatically sanitized // pass form component to process database binding on all children $this->page->insertData($this->frtMain->getComponent("userForm")); // Inserts into bound tables // Redirect or show success $this->page->forward(getEventURLSecure("view", $this->getInsertId())); } // Read (View) public function page_event_view($recordId) { // Auto-populate Components from database // pass form component to process database binding on all children $this->page->viewData($this->frtMain->getComponent("userForm"), $recordId); $this->setFrontFile($this->frtMain); } // Update public function page_event_update($evtp) { // Components have values from form // Update based on existing record detection // pass form component to process database binding on all children $this->page->updateData($this->frtMain->getComponent("userForm")); // Updates bound tables $this->page->forward(getEventURLSecure("view", $this->getUpdateId())); } // Delete public function page_event_delete($evtp) { $recordId = (int)$this->page->evtp; $this->page->deleteRec($recordId); // Deletes from bound tables $this->page->forward(getGateURL("user")); } ``` ### **6.6.3 Manual Database Operations** For more control, work with Components directly: ```php // Get value formatted for database $dbValue = $component->getSqlSafeValue(); // Set value from database result $component->setFromDatabase($row); // Check if Component has database binding if ($component->hasDatabaseBinding()) { $table = $component->dtable; // if empty then use SphpBase::page()->tblName property $field = $component->dfield; // if empty then don't bind with database // Build SQL manually SphpBase::dbEngine()->connect(); // if not connected then connect // component value are safe but still can break query // so use SphpBase::dbEngine()->cleanQuery is more safer but need DB connection $sql = "INSERT INTO $table ($field) VALUES ('" . SphpBase::dbEngine()->cleanQuery($component->getValue()) ."')"; SphpBase::dbEngine()->executeQuery($sql); // or use easy other methods // generate sql from inserSQL method $sql = SphpBase::dbEngine()->insertSQL([$field=>$component->getValue()],$table); SphpBase::dbEngine()->executeQuery($sql); // or use single method runSQL $inserted_id = SphpBase::dbEngine()->runSQL($table,[$field=>$component->getValue()]); } ``` ### **6.6.4 Complex Binding Scenarios** **Multiple table binding:** ```html public function page_event_insert($evtp) { /** * We can also use manual execute query with SphpBase::dbEngine()->runSQL() but here we * use SphpBase::page()->insertData() method. */ // Insert into users table $userId = $this->page->insertData($this->frtMain->tbluser); // Insert into profiles with user_id $this->frtMain->getComponent("txtBio")->setValue($userId); // Insert into tblprofile table $this->page->insertData($this->frtMain->tblprofile); } ``` **Conditional binding:** ```php // In Gate lifecycle event public function comp_txtRole_on_init($evtp) { $component = $evtp['obj']; // Only bind to database for admin users if ($this->page->isAuthenticate("ADMIN")) { // disable all DB operations (view,insert,update) if not ADMIN $component->bindToTable("users", "role"); } } ``` ## **6.7 Creating Custom Components** When built-in Components aren't enough, create your own. ### **6.7.1 Basic Custom Component** ```php // components/StarRating.php namespace MyApp\Components; class StarRating extends \Sphp\tools\Component { private $maxStars = 5; private $currentRating = 0; public function oncreate($element){ if($this->issubmit){ // when hidden field submit as Component Name then Component Class read value // so restore currentRating from value submitted $this->setValue($this->value); } } // Parse Phase Fusion method public function fi_setMaxStars($value) { $this->maxStars = (int)$value; } // Render Phase Fusion method public function fu_setRating($value) { $this->currentRating = (float)$value; } // Override render method public function onrender() { $html = '
'; for ($i = 1; $i <= $this->maxStars; $i++) { $filled = $i <= $this->currentRating; $class = $filled ? 'star-filled' : 'star-empty'; $html .= ""; } $html .= '
'; // remove name from Component Tag becuase use in hidden field $this->setHTMLName(""); // remove HTML Tag name attribute from output // Set as Component output $this->element->setInnerHTML($html); // change Tag Name as Valid HTML Tag $this->tagName = "div"; } // Handle posted value in oncreate public function getValue() { return $this->currentRating; } public function setValue($value) { $this->currentRating = min($this->maxStars, max(0, (float)$value)); } } ``` **Usage in FrontFile:** ```html ``` ### **6.7.2 Component with JavaScript** ```php // components/AutoComplete.php class AutoComplete extends \Sphp\Comp\Form\TextField { private $sourceUrl = ''; private $minChars = 2; public function fi_setSource($url) { $this->sourceUrl = $url; } public function fi_setMinChars($chars) { $this->minChars = (int)$chars; } public function onrender() { // Call parent to render input field parent::onrender(); // always use $this->name on server side to create id and name attributes of HTML Tag // Add autocomplete container $container = '
'; $this->element->setPostTag($container); // Add JavaScript initialization $js = " $('#{$this->name}').autocomplete({ source: '{$this->sourceUrl}', minChars: {$this->minChars}, containerId: '{$this->name}_results' }); "; addHeaderJSCode($this->name . '_autocomplete', $js); } } ``` ### **6.7.3 Component Resource Management** Components can manage their own CSS/JS resources: ```php class DateRangePicker extends \Sphp\tools\Component { public function onjsrender() { // Load Component-specific CSS addFileLink($this->myrespath . '/daterangepicker.css'); // Load Component-specific JS addFileLink($this->myrespath . '/daterangepicker.js'); /** SphpBase::SphpJsM() manage only few required css,js libraries. Bootstrap 5, jQuery 3, * fontawesome 6 are included for all projects. Other Library jQuery UI and jQuery-mobile we can add * according to requirements. But For all other libraries it is Component responsibilities to use * downloaded files or CDN links or Custom Master File responsibilities to download all required libraries. * So SartajPHP don't provide all libraries sources, it is only provide management API. So you can * use addFileLink function to inject js or css files. */ // Load dependency (jQuery UI) SphpBase::SphpJsM()::addjQueryUI(); } public function onrender() { $startId = $this->name . '_start'; $endId = $this->name . '_end'; $html = "
to
"; $this->element->setInnerHTML($html); // Initialize picker $js = "$('#{$startId}, #{$endId}').daterangepicker();"; addHeaderJSFunctionCode("ready",$this->name,$js); } } ``` ## **6.8 Advanced Component Patterns** ### **6.8.1 Composite Components** Components that contain other Components: ```php class AddressForm extends \Sphp\tools\Component { private $streetComponent; private $cityComponent; private $stateComponent; /** @var \Sphp\tools\FrontFile */ private $front1 = null; public function oninit() { // Create child Components programmatically $str1 = ''; $str1 .= ''; $str1 .= ''; $str1 .= '

Access Parent Front File Component: ##{$'. $this->name .'->getAttribute("class")}#

'; // private Front File, Components can't access in Parent FrontFile //$this->front1 = $this->createFrontObjectPrivate($str1,true); // share Front File, Components can access in Parent FrontFile // and also parent components can access in child FrontFile // database binding also works in shared FrontFile // shared FrontFile use prefix as "name + _" of parent to avoid id/name conflicts $this->front1 = $this->createFrontObjectShare($str1,true); $this->streetComponent = $this->front1->getComponent($this->name. '_street'); $this->cityComponent = $this->front1->getComponent($this->name. '_city'); $this->stateComponent = $this->front1->getComponent($this->name. '_state'); // parent FrontFile can access child Front File Components with proper prefix $this->frontobj->getComponent($this->name.'_street')->fi_setDefaultValue('84 Bell'); $this->stateComponent->fi_setDefaultValue('Ohio'); // ... more child Components } public function onrender() { // Render child Components $str1 =$this->front1->ProcessMe(); $this->setInnerHTML($str1); } public function getValue() { // Return composite value return [ 'street' => $this->streetComponent->getValue(), 'city' => $this->cityComponent->getValue(), 'state' => $this->stateComponent->getValue() ]; } public function setValue($data) { // Distribute composite value if (is_array($data)) { $this->streetComponent->setValue($data['street'] ?? ''); $this->cityComponent->setValue($data['city'] ?? ''); $this->stateComponent->setValue($data['state'] ?? ''); } } } ``` ### **6.8.2 Data-Aware Components** Components that fetch their own data: ```php class CategorySelect extends \Sphp\Comp\Form\Select { // query execute on render time save useless processing when no rendering required public function onprerender() { // Fetch categories from database $db = SphpBase::dbEngine(); $categories = $db->executeQuery( "SELECT id, name FROM categories WHERE active = 1 ORDER BY name" ); // Build options array $options = []; foreach ($categories as $cat) { $options[] = [$cat['id'],$cat['name']]; } // Set options $this->setOptionsKeyArray(); $this->setOptionsArray($options); parent::onprerender(); } } ``` ### **6.8.3 Stateful Components** Components that maintain state across requests: ```php class ShoppingCart extends \Sphp\tools\Component { private $sessionKey; public function oninit() { $this->sessionKey = 'cart_' . $this->id; // Initialize from session if (!(SphpBase::sphp_request()->isSession($this->sessionKey))) { SphpBase::sphp_request()->session($this->sessionKey, []); } } public function addItem($productId, $quantity = 1) { $cart = SphpBase::sphp_request()->session($this->sessionKey); if (isset($cart[$productId])) { $cart[$productId] += $quantity; } else { $cart[$productId] = $quantity; } SphpBase::sphp_request()->session($this->sessionKey, $cart); } public function getValue() { return SphpBase::sphp_request()->isSession($this->sessionKey) ?? []; } public function setValue($cartData) { SphpBase::sphp_request()->session($this->sessionKey, $cartData); } public function onrender() { $cart = $this->getValue(); $itemCount = array_sum($cart); $total = $this->calculateTotal($cart); $html = "
{$itemCount} items \${$total} View Cart
"; $this->element->setInnerHTML($html); } } ``` ## **6.9 Component Validation System** ### **6.9.1 Built-in Validation Rules** ```html ``` ### **6.9.2 Custom Validation Methods** ```php include_once(SphpBase::sphp_settings()->comp_uikit_path . "/form/TextField.php"); class CustomTextField extends \Sphp\Comp\Form\TextField { private $usernameFormat = false; private $uniqueUsername = false; // User Name Format validation method public function fi_setUsernameFormat() { // true = User Name Format validation enabled $this->usernameFormat = true; // check server side validation when Component is submitted if ($this->issubmit) { // Must start with letter // check server side validation if (!preg_match('/^[a-zA-Z]/', $this->value)) { // inform user and Framework about the Validation error $this->setErrMsg( $this->getAttribute("msgname") . " Username must start with a letter"); } // Only letters, numbers, underscore // check server side validation if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->value)) { // inform user and Framework about the Validation error $this->setErrMsg( $this->getAttribute("msgname") . " Username can only contain letters, numbers, and underscore"); } } } /** User Name Existence validation (check against database) * we will check database on client side validation with * use AJAX call to server event and handle in this file */ public function fi_setUniqueUsername() { // true = Unique User Name validation enabled $this->uniqueUsername = true; // check server side validation when Component is submitted if ($this->issubmit) { if ($this->findUniqueUsername()) { // inform user and Framework about the Validation error $this->setErrMsg($this->getAttribute("msgname") . " Username already taken"); } } } private function findUniqueUsername() { $db = SphpBase::dbEngine(); return $db->isRecordExist( "SELECT COUNT(*) FROM ". $this->dtable ." WHERE ". $this->dfield ." = '". $this->value ."'", ); } protected function onprerender() { // Add client side validation with javascript if ($this->usernameFormat) { // insert javascript code into form component submit event addHeaderJSFunctionCode("{$this->formName}_submit", "{$this->name}usernameformat", ' var usernameformat = /^[a-zA-Z][a-zA-Z0-9_]*$/; var blnusernameformat = usernameformat.test(document.getElementById("'. $this->name .'").value); if(!blnusernameformat){ // show validation error message displayValidationError(document.getElementById("'. $this->name .'"),"Value isn\'t match with User Name Format "); // prevent form submit blnSubmit = false ; }'); } // For AJAX based Unique User Name validation // we call AJAX function on keyup event to server event to check username existence if ($this->uniqueUsername) { // insert javascript into jQuery ready event handler addHeaderJSFunctionCode("ready", "{$this->name}uniqueusername", ' $("#'. $this->name .'").keyup(function(){ // check username existence with call getAJAX function to server event var data = {}; data["'. $this->name .'"] = $("#'. $this->name .'").val(); getAJAX("'. getEventURL("checkusername") .'",data,true,function(response){ // process server response if(response.exists){ // show validation error message displayValidationError(document.getElementById("'. $this->name .'"),"Username already taken"); // disable form submit blnSubmit = false ; } else { // clear validation error message clearValidationError(document.getElementById("'. $this->name .'")); } }); });'); } parent::onprerender(); } /** now we handle server event and reply AJAX validation request. * So we override onappevent event handler to handle server event. */ protected function onappevent() { // check if it is validation request with use of Page Class: SphpBase::page() if(SphpBase::page()->getEvent() == "checkusername") { // check username existence with call findUniqueUsername function $this->findUniqueUsername() ? SphpBase::JSServer()->addJSONReturnBlock(['exists' => true]) : SphpBase::JSServer()->addJSONReturnBlock(['exists' => false]); } } } ``` **Usage:** ```html ``` ### **6.9.3 Validation Groups** ```html

``` ```php // In App public function page_event_validate_group($evtp) { $group = $evtp; // "personal" or "contact" $personal = $this->frtMain->getComponent($group); // Validate only Components in this group $isValid = false; // we validate name should not contain @ foreach($personal->getChildren() as $compname=>$compobj){ $isValid = $isValid || (strpos($compobj->value,'@') > 0) ? false : true ; } $strError = ""; if(! $isValid) $strError = "Error in Validation"; // send error to display in tag with id='err' $this->JSServer->addJSONHTMLBlock('err',$strError); } ``` ## **6.10 Performance Optimization** ### **6.10.1 Component Caching** ```php class CachedComponent extends \Sphp\tools\Component { private $cacheKey; private $cacheTtl = 300; // 5 minutes public function oninit() { $this->cacheKey = 'component_' . $this->name; } public function getCachedData() { $sphp_api = SphpBase::sphp_api(); if ($data = $sphp_api->readFromCache($this->cacheKey,$this->cacheTtl)) { return $data; } // Expensive operation $data = $this->fetchDataFromExternalAPI(); // $data save to Cache $sphp_api->saveToCache($this->cacheKey, $data); return $data; } public function clearCache() { SphpBase::sphp_api()->clearCache($this->cacheKey); } } ``` ### **6.10.2 Lazy Loading Components** ```html
Loading component...
``` ```php class HeavyComponent extends \Sphp\tools\Component { public function onrender() { if ($this->shouldLoad()) { // Expensive initialization $this->initializeHeavyResources(); $html = $this->generateContent(); } else { // Show placeholder $html = $this->element->getInnerHTML(); } $this->element->setInnerHTML($html); } private function shouldLoad() { // Check if Component is in viewport // Or if user has interacted with it return $this->isVisible() || $this->hasInteraction(); } } ``` ## **6.11 Debugging Components** ### **6.11.1 Component Inspection** ```php // In Gate during development public function onready() { if ($this->debug->debugmode > 1) { // Dump all Components foreach ($this->frtMain->getComponents() as $component) { $this->debug->println( "Component: " . $component->id . " | Type: " . get_class($component) . " | Value: " . $component->getValue() ); } } } ``` ### **6.11.2 Browser Console Debugging** Components can send debug info to browser console: ```php public function onrender() { if ($this->debug->debugmode > 0) { $debugInfo = [ 'id' => $this->id, 'value' => $this->getValue(), 'valid' => $this->isValid(), 'bound' => $this->hasDatabaseBinding() ]; $js = "console.debug('Component Debug', " . json_encode($debugInfo) . ");"; $this->element->setPreTag(""); } } ``` ### **6.11.3 Common Issues and Solutions** **Issue: Component not updating** ```html ``` **Issue: Client Side Validation not working** ```html ``` **Issue: Database binding good but value isn't inserted in Database** ```html
``` **Issue: Database binding failing** ```html ``` ## **6.12 Real-World Component Examples** ### **6.12.1 Image Upload with Preview** ```php class ImageUploadWithPreview extends \Sphp\Comp\Form\FileUploader { public function onrender() { // Render file input parent::onrender(); // Add preview container $previewId = $this->name . '_preview'; $preview = "
"; // Add preview JavaScript $js = " $('#{$this->name}').on('change', function(e) { var file = this.files[0]; if (file) { var reader = new FileReader(); reader.onload = function(e) { $('#{$previewId}').html( '' ); } reader.readAsDataURL(file); } }); "; $this->element->setPostTag($preview); // jQuery code should be inside jQuery ready // give name `$this->id . '_preview_js'` to code to use for update SphpBase::sphp_api()->addHeaderJSFunctionCode("ready",$this->id . '_preview_js', $js); } } ``` ### **6.12.2 Rich Text Editor** ```php class RichTextEditor extends \Sphp\Comp\Form\TextArea { private $toolbar = 'basic'; public function fi_setToolbar($type) { $this->toolbar = $type; } public function onrender() { // Get TextArea HTML parent::onrender(); // Convert to rich editor $editorId = $this->name . '_editor'; $js = " tinymce.init({ selector: '#{$this->name}', height: 300, menubar: false, toolbar: '{$this->getToolbarConfig()}', plugins: 'link image code' }); "; // Load TinyMCE if not already loaded if (!SphpBase::sphp_api()->issetFileLink('tinymce')) { // external url need permission in prerun.php file SphpBase::sphp_api()->addFileLink( 'https://cdn.tiny.cloud/1/your-api-key/tinymce/6/tinymce.min.js', true, 'tinymce', 'js' ); } SphpBase::sphp_api()->addHeaderJSFunctionCode("ready",$this->id . '_tinymce', $js); } private function getToolbarConfig() { switch ($this->toolbar) { case 'basic': return 'undo redo | bold italic | link'; case 'full': return 'undo redo | formatselect | bold italic underline | ' . 'alignleft aligncenter alignright | bullist numlist | ' . 'link image | code'; default: return 'undo redo | bold italic'; } } } ``` ## **6.13 Chapter Summary** Components are the heart of SartajPHP's server-side UI architecture: 1. **Active Participants**: Components are intelligent objects with state, behavior, and lifecycle 2. **HTML-PHP Bridge**: They seamlessly connect FrontFile markup with server logic 3. **Database Integration**: Automatic CRUD operations through simple binding 4. **Validation System**: Built-in and extensible validation rules 5. **Customizable**: Create your own Components for specialized functionality Key concepts to master: - **Lifecycle Events**: `oninit()`, `oncreate()`, `onrender()` - **Fusion Attributes**: `fui-*`, `fun-*`, `fur-*` for FrontFile control - **Database Binding**: `dtable` and `dfield` for automatic CRUD - **Value Management**: `getValue()`, `setValue()`, validation - **Custom Components**: Extend `Component` class for specialized needs Components transform SartajPHP from a simple templating system into a powerful application framework. With Components handling the heavy lifting of data binding, validation, and UI logic, you can focus on building your application's unique features. In the next chapter, we'll explore Fusion Attributes in depth—understanding exactly how they control Component behavior and execution timing. --- *Next: Chapter 7 dives deep into Fusion Attributes, explaining the precise timing and usage rules that make Components so powerful.* # **Chapter 7: Fusion Attributes - Precision Control of Component Behavior** ## **7.1 The Fusion Attribute Philosophy: Declarative Component Control** Fusion attributes represent SartajPHP's revolutionary approach to connecting FrontFile markup with Component behavior. Unlike traditional frameworks where you call methods in PHP code, Fusion attributes allow you to **declare Component behavior directly in HTML** using a clean, declarative syntax. ### **7.1.1 The Problem with Traditional Approaches** Consider how other frameworks handle component configuration: **Imperative PHP approach (traditional):** ```php // In controller/template $input = new TextField(); $input->setId('username'); $input->setRequired(true); $input->setMinLength(3); $input->setDefaultValue('Guest'); $input->setValidationMessage('Username is required'); echo $input->render(); ``` **Template directive approach (Blade/Angular):** ```html
Username is required
``` Both approaches have limitations: - **Imperative PHP**: Logic mixed with presentation, hard to maintain - **Template directives**: Framework-specific syntax, learning curve ### **7.1.2 The Fusion Attribute Solution** SartajPHP Fusion attributes provide a third way: ```html ``` **What makes Fusion attributes special:** 1. **Declarative**: Describe what you want, not how to do it 2. **HTML-compatible**: Uses standard attribute syntax 3. **Timing-aware**: Different prefixes for different execution phases 4. **Type-safe**: Values are properly typed for each method 5. **Discoverable**: IDE autocomplete can suggest available methods ### **7.1.3 The Mental Model: Method Calls as Attributes** Think of Fusion attributes as **method calls translated into HTML attributes**: ``` Component Method: $component->fi_setRequired() FrontFile Attribute: fui-setRequired="" Component Method: $component->fu_setValue("Hello") FrontFile Attribute: fur-setValue="Hello" Component Method: $component->fu_setValue($dynamicVar) FrontFile Attribute: fur-setValue="##{$dynamicVar}#" ``` This translation happens automatically during FrontFile processing. ## **7.2 The Three Fusion Prefixes: Timing is Everything** The most crucial concept in Fusion attributes is **execution timing**. Different prefixes execute at different phases of the Component lifecycle. Fusion Attribute `fui-*` execute Fusion Methods of type `fi_` at parse phase and `fur-*` execute Fusion Methods type `fu_` at render phase. Fusion Attribute `fun-*` execute type `fi_` Fusion Methods on Parse Phase and type `fu_` Fusion Methods at Render Phase. So Fusion Attribute `fun-*` can identify automatic execution time and it is safe if you don't know how to call a fusion method. ### **7.2.1 Execution Phase Overview** ``` Component Lifecycle: ┌─────────────────────────────────────────────────────┐ │ │ │ PARSE PHASE (Initialization) │ │ ├── FrontFile parsed │ │ ├── Components created │ │ ├── fui-* attributes executed │ │ └── fun-* attributes executed type fi_* method │ │ │ │ REQUEST PROCESSING │ │ ├── Form values populated │ │ ├── Validation performed │ │ └── Gate logic executes │ │ │ │ RENDER PHASE (Output Generation) │ │ ├── fur-* attributes executed │ │ ├── fun-* attributes executed type fu_* method │ │ └── HTML generated │ │ │ └─────────────────────────────────────────────────────┘ ``` ### **7.2.2 fui-* (Parse Phase - "Initialization")** **Meaning:** "Execute this during Component initialization" **Characteristics:** - Executes **once** when Component is created - Runs **before** any user input is processed - Used for **setup, configuration, validation rules** - Calls `fi_` prefixed methods in Component class **When to use:** - Setting validation rules (`fui-setRequired`, `fui-setEmail`) - Configuring Component behavior (`fui-setForm`, `fui-setMsgName`) - Setting default values that don't change (`fui-setDefaultValue`) - Database binding configuration **Examples:** ```html ``` **Behind the scenes:** ```php // When FrontFile processes fui-setRequired="" $component->fi_setRequired(); // When FrontFile processes fui-setMinLen="3" $component->fi_setMinLen(3); // When FrontFile processes fui-setDefaultValue="United States" $component->fi_setDefaultValue("United States"); ``` ### **7.2.3 fur-* (Render Phase - "Runtime")** **Meaning:** "Execute this during Component rendering" **Characteristics:** - Executes **every time** Component renders - Runs **after** request processing, before HTML generation - Used for **dynamic values, conditional output** - Calls `fu_` prefixed methods in Component class **When to use:** - Setting values based on dynamic data (`fur-setValue`) - Conditional attribute setting (`fur-_class`, `fur-_style`) - Runtime configuration changes - User-specific content **Examples:** ```html
``` **Behind the scenes:** ```php // When FrontFile processes fur-setValue="##{$user->email}#" // $user->email is evaluated, then: $component->fu_setValue($evaluatedValue); // When FrontFile processes fur-_class="active" $component->element->setAttribute("class", "active"); ``` ### **7.2.4 fun-* (Auto Phase - "Smart")** **Meaning:** "Let the framework decide when to execute" **Characteristics:** - **Fusion Method of type fi_** execute in Parse Phase (like `fui-*`) - **Fusion Method of type fu_** execute in Render Phase (like `fur-*`) - **Auto-detects** based on call of type of Fusion Method - Can call both `fi_` and `fu_` methods depending on timing **When to use:** - When you're not sure about timing - For simple cases where timing doesn't matter - When writing generic Components/templates **Examples:** ```html
``` ### **7.4.2 Execution Timing in Different Prefixes** **With `fui-*` (Parse Phase):** ```html ``` The expression `date('Y-m-d')` is evaluated when the Component is created, not when it renders. **With `fur-*` (Render Phase):** ```html ``` The expression `date('Y-m-d')` is evaluated fresh each time the Component renders. **With `fun-*` (Auto-detected):** ```html ``` ### **7.4.3 Variable Scope in Expressions** Expression tags have access to specific variables: ```html ``` ### **7.4.4 Complex Expressions and Limitations** **Simple expressions work:** ```html
``` **Complex logic should be in App:** ```html ``` ### **7.4.5 Common Fusion Issues and Solutions** **Issue: Fusion attribute not executing** ```html ``` **Issue: Expression not evaluating in fui-*** ```html ``` **Issue: Method not found error** ```php // In Component class class MyComponent extends Component { // ❌ Missing fi_/fu_ prefix public function setRequired() { /* ... */ } // ✅ Correct prefix public function fi_setRequired() { /* ... */ } } ``` ## **7.5 Best Practices** ### **7.5.1 Organization Guidelines** 1. **Group related attributes:** ```html fui-setRequired="" fui-setEmail="" fui-setForm="myForm" fui-setMsgName="Email" fui-setDefaultValue="" fur-setValue="##{$email}#" fur-_class="##{$emailClass}#"> ``` 2. **Use consistent ordering:** - `id` and `runat="server"` first - HTML attributes next (`type`, `class`, etc.) - `fui-*` attributes (Parse Phase) - `fur-*` attributes (Render Phase) - `fun-*` attributes (if used) - Event hooks (`on-init`, etc.) ### **7.5.2 Naming Conventions** **For custom Fusion methods:** ```php // Clear, descriptive names public function fi_setMaximumFileSize($size) { /* ... */ } public function fu_setProgressPercentage($percent) { /* ... */ } // Use consistent prefixes public function fi_set...() // Parse Phase public function fu_set...() // Render Phase ``` ### **7.5.3 Security Considerations** ```html
##{$userContent}#
``` ## **7.6 Chapter Summary** Fusion attributes are SartajPHP's sophisticated system for declarative Component control: 1. **Three Prefixes, Three Timings:** - `fui-*`: Parse Phase (setup, validation, static config) - `fur-*`: Render Phase (dynamic values, runtime attributes) - `fun-*`: Auto-detected (simple cases, generic templates) 2. **Method Prefix Compatibility:** - `fi_` methods ← `fui-*` attributes (Parse Phase only) - `fu_` methods ← `fur-*` attributes (Render Phase preferred) - The `_` method ← generic attribute setting 3. **Expression Tag Integration:** - `##{}#` tags evaluated at attribute execution time - Access limited to safe variables (`$parentapp`, `$frontobj`, etc.) - Keep expressions simple; complex logic belongs in App 4. **Best Practices:** - Use `fui-*` for setup, `fur-*` for runtime - Group attributes by phase - Keep expressions in Fusion attributes simple - Pre-calculate complex values in App Fusion attributes give you precise control over when and how Component methods execute, enabling you to build complex, dynamic UIs with clean, declarative HTML. By mastering Fusion attributes, you unlock the full power of SartajPHP's Component system. In the next chapter, we'll explore Expression Tags in depth—understanding their evaluation rules, scope limitations, and advanced patterns for dynamic content. --- *Next: Chapter 8 dives deep into Expression Tags, covering evaluation rules, variable scope, and advanced patterns for dynamic content in FrontFiles.* # **Chapter 8: Expression Tags - Controlled Dynamic Content** ## **8.1 The Philosophy: Safe, Limited Template Expressions** Expression tags in SartajPHP represent a carefully designed compromise between power and safety. Unlike PHP's unrestricted `` tags or template engines that allow arbitrary code execution, SartajPHP's expression tags provide a **controlled, limited subset of PHP syntax** specifically designed for template logic. ### **8.1.1 The Security Problem with Full PHP in Templates** Consider the dangers of allowing full PHP in templates: ```php ``` Even without malicious intent, template PHP often becomes messy: ```php isPremium()) { $discount = applyPremiumDiscount($total); } echo formatCurrency($total - $discount); ?> ``` ### **8.2.2 The SartajPHP Solution: Limited but Powerful** SartajPHP expression tags provide just enough power for presentation logic while preventing security issues and messy code: ```html #{$userId = $parentgate->getUserIdFromSession()}#

Order Total: ##{$parentgate->formatCurrency($orderTotal)}#

#{if $parentgate->isPremium() }#

Premium Discount Applied: ##{$parentgate->formatCurrency($premiumDiscount)}#

#{endif}#

Final Amount: ##{$parentgate->formatCurrency($orderTotal - $discount)}#

``` **Key limitations that ensure safety:** 1. **No function definition** (can't create new functions) 2. **No class definition** (can't create new classes) 3. **Limited function calls** (only allowed functions) 4. **No superglobal access** (no `$_GET`, `$_POST`, etc.) 5. **Controlled variable scope** (limited to template context) ## **8.2 Expression Tag Syntax and Types** ### **8.2.1 Two Tag Types** SartajPHP provides two expression tag formats for different purposes: **Output Tags (`##{ }#`) - Display Results:** ```html

Welcome, ##{$userName}#!

Total: $##{calculateTotal($items)}#

Status: ##{$isActive ? 'Active' : 'Inactive'}#

``` **Silent Tags (`#{ }#`) - Execute Without Output:** ```html #{ $counter = 0; }# #{ $items = getItems(); }# #{ $user = loadUserData(); }# #{ $fullName = $firstName . ' ' . $lastName; }#

Hello, ##{$fullName}#

``` ### **8.2.2 Output Modifiers** Expression tags support output modifiers for different contexts: ```html

##{$userInput}#

##{raw:$trustedHtml}#

Search ``` ### **8.2.3 Tag Nesting and Combination** Expression tags can be combined but not nested: ```html

##{ "Result: " . ##{$value}# }#

##{ "Result: " . $value }#

#{ $prefix = "Result: "; }#

##{$prefix . $value}#

##{ "User: " . $user->name . " (" . ($user->isActive ? 'Active' : 'Inactive') . ")" }#

``` ## **8.3 Supported Syntax and Features** Based on provided examples, here's what SartajPHP expression tags support: ### **8.3.1 Variable Assignment and Arithmetic** ```html #{ $x = 3; $y = 4; }#

Calculation: ##{$x - ($y * 2)}#

Multiplication: ##{$x * 8}#

#{ $a = 10; $b = 20; $c = 30; }#

Complex: ##{($a + $b) * $c}#

Mixed: ##{$a * $b + $c / 2}#

#{ $counter = 0; $total = 0; }# #{ $counter = $counter + 1; $total = $total + 10; }# #{ $counter = $counter + 1; $total = $total + 20; }#

Count: ##{$counter}#, Total: ##{$total}#

``` ### **8.3.2 String Operations** ```html #{ $firstName = "John"; $lastName = "Doe"; }#

Full Name: ##{$firstName . " " . $lastName}#

#{ $text = "hello world"; }#

Original: ##{$text}#

Uppercase: ##{strtoupper($text)}#

Length: ##{strlen($text)}#

Substring: ##{substr($text, 0, 5)}#

Welcome ##{$firstName}# ##{$lastName}#!

``` ### **8.3.3 Control Structures** **If/Elseif/Else Statements:** ```html #{ $a = 2; $b = 2; }#
#{if $a != 3 }#
if worked (a != 3)
#{elseif $b == 2 }#
elseif worked (b == 2)
#{else}#
else worked
#{endif}#
#{ $isLoggedIn = true; $userRole = 'admin'; }#
#{if $isLoggedIn && $userRole === 'admin' }#

Welcome Admin!

#{endif}#
#{ $x = 5; $y = 10; }#
#{if $x > 0 }# #{if $y > 5 }# Both conditions true #{endif}# #{endif}#
``` **Ternary Operator:** ```html #{ $status = "active"; $points = 75; }#

Status: ##{($status == "active") ? "Online" : "Offline"}#

Result: ##{($points > 50 && $status == "active") ? "Pass" : "Fail"}#

##{true ? "A" : (false ? "B" : "C")}#

##{true ? (false ? "B" : "C") : "D"}#

#{ $a = 1; $b = 2; $c = 3; $d = 4; $e = 6; $f = 6; }#

##{ $a > $b ? ($c < $d ? "X" : "Y") : ($e == $f ? "Z" : "W") }#

``` ### **8.3.4 Function Calls** Expression tags support a limited set of PHP functions: ```html

Current Date: ##{date('Y-m-d')}#

Formatted: ##{date('F j, Y, g:i a')}#

Timestamp: ##{time()}#

#{ $filename = "document.pdf"; }#

Extension: ##{pathinfo($filename, PATHINFO_EXTENSION)}#

Basename: ##{basename($filename)}#

Square root: ##{sqrt(16)}#

Absolute: ##{abs(-5)}#

Round: ##{round(3.14159, 2)}#

#{ $colors = ["red", "green", "blue"]; }#

Count: ##{count($colors)}#

First: ##{$colors[0]}#

``` ### **8.3.5 Comparison and Logical Operators** ```html #{ $price = 100; $quantity = 3; $inStock = true; $onSale = false; }#

##{$price > 50 ? "Expensive" : "Cheap"}#

##{$quantity >= 1 ? "In cart" : "Empty"}#

##{$price == 100 ? "Exactly 100" : "Not 100"}#

##{$price != 50 ? "Not 50" : "Is 50"}#

##{$inStock && $quantity > 0 ? "Available" : "Unavailable"}#

##{$inStock || $onSale ? "Can purchase" : "Cannot purchase"}#

##{!$onSale ? "Regular price" : "On sale"}#

##{ ($price < 200 && $inStock) || $onSale ? "Good deal" : "Consider carefully" }#

``` ## **8.4 Variable Scope and Availability** Understanding what variables are available in expression tags is crucial. ### **8.4.1 Predefined Variables** These variables are always available in expression tags: ```html

Site: ##{$parentgate->siteName}#

User: ##{$parentgate->currentUser->name}#

Template: ##{$frontobj->getFilePath()}#

Base URL: ##{$sphp_settings->getBase_url()}#

Version: ##{$sphp_settings->getVersion()}#

Title: ##{$metadata->get('title')}#

Description: ##{$metadata->get('description')}#

Title: ##{$title}#

``` ### **8.4.2 App-Provided Variables** Variables can be passed from Gate to FrontFile: **In App:** ```php public function page_new() { // Set variables for FrontFile $this->frtMain->addProp('pageTitle', 'Welcome Page'); $this->frtMain->addProp('userList', $this->getUsers()); $this->frtMain->addProp('currentTime', time()); // Also available as properties $this->pageTitle = 'Welcome Page'; $this->setFrontFile($this->frtMain); } ``` **In FrontFile:** ```html

##{$pageTitle}#

Loaded at: ##{date('H:i:s', $currentTime)}#

``` ### **8.4.3 Variable Inheritance Chain** Variables are resolved in this order: 1. **Local variables** (defined in same Front File) 2. **FrontFile properties** (set via `addProp()` or `$frontobj->var`) 3. **Gate properties** (set via `$this->var` in App) 4. **Predefined variables** (`$parentapp`, `$frontobj`, etc.) ```html #{ $title = "Local Title"; }#

##{$title}#

##{$pageTitle}#

##{$txtname->getValue()}#

``` ### **8.4.4 Scope Limitations** **Variables DO persist** within the same FrontFile: ```html #{ $counter = 0; }#

First: ##{$counter}#

#{ $counter = $counter + 1; }#

Second: ##{$counter}#

``` **Variables DO NOT persist** across different FrontFiles or requests. **Variables CANNOT access:** ```html

##{$_GET['param']}#

##{for($c=0;$c<9;$c++>){}}#

##{new DateTime()}#

``` ## **8.5 Execution Timing and Order** Expression tags execute at specific times during FrontFile processing, which affects what data is available. ### **8.5.1 Three Execution Contexts** **Context 1: Fusion Attributes and code tag on-init="true" Parse time** ```html $counter=1 : ##{$counter=1}# ``` **Context 2: Fusion Attributes and code tag on-endrender="true" Render time** ```html
##{$showall->getRow('profile_name')}#
$counter=4 : ##{$counter=$counter+1;$counter}# ``` **Context 3: Normal HTML Content execute after render** (Lowest Priority) ```html

Welcome, ##{$userName}#!

Today is ##{date('Y-m-d')}#

``` - Executes **after all Components have rendered** - Cannot affect Component rendering - Good for final output composition ### **8.5.2 Practical Timing Example** ```html

Outside: $counter=6 : ##{$counter=$counter+1;$counter}#

$counter=1 : ##{$counter=1}#
$counter=4 : ##{$counter=$counter+1;$counter}#
$counter=3 : ##{$counter=$counter+1;$counter}#
$counter=7 : ##{$counter=$counter+1;$counter}#

Final: $counter=8 : ##{$counter=$counter+1;$counter}#

Outside: $counter=9 : ##{$counter=$counter+1;$counter}#

``` **Expected output:** - `code` Top code bloack first evaluate and create,set and print $counter value: "1" (Parse Phase) - `div` data-message: "5" (Render Phase) - "Final: 8" (after render) - "Outside: 9" (normal HTML) ### **8.5.3 The Rule of Thumb** > **Expression tags in normal HTML execute AFTER everything else.** > > If you need to affect Component rendering, use Fusion attributes (`fur-*`). > If you need early execution, use `fui-*` or do it in the App. > if you need execution in sequence of Components use code block with specific event bind ## **8.6 Advanced Patterns and Techniques** ### **8.6.1 Template Loops (Simulated)** While expression tags don't support `for` or `foreach` loops directly, you can simulate them: **Method 1: Using App-prepared data:** ```html public function loopgen() { $items = ['Apple', 'Banana', 'Cherry']; $strout = ""; foreach($items as $i=>$item){ $strout .= '
  • '. $item . '
  • '; } return $strout; }
      ##{raw:$parentgate->loopgen()}#
    ``` **Method 2: Using Component for loops:** ```html #{$items = ['Apple', 'Banana', 'Cherry']}#

    Item ##{$items[$loop1->counter]}#

    ``` ### **8.6.2 Complex Condition Styling** ```html #{ $userStatus = 'active'; $unreadCount = 5; $isAdmin = true; }#
    ##{$username}# #{if $unreadCount > 0 }# ##{$unreadCount}# #{endif}# #{if $isAdmin }# ⚙️ #{endif}#
    ``` ### **8.6.3 Dynamic Attribute Generation** ```html #{ $buttonId = "btnSubmit"; $buttonClass = "btn-primary"; $isDisabled = false; $dataAttrs = [ 'user-id' => 123, 'action' => 'submit', 'confirm' => 'true' ]; $str2 = ''; }#
    ##{$str2 = $str2 . ' ' . $loop1->key . ' = "' . $loop1->item . '"'}#
    #{$str1 = ''}# ##{raw:$str1}# ``` ## **8.7 Performance Optimization** ### **8.7.1 Minimize Expression Complexity** ```html

    ##{ // This recalculates every time $total = 0; foreach ($items as $item) { $total += $item->price * $item->quantity; } $total = $total * (1 - $discount); formatCurrency($total); }#

    ##{formatCurrency($precalculatedTotal)}#

    public function page_new() { $total = $this->calculateOrderTotal($items); $this->frtMain->addProp('precalculatedTotal', $total); } ``` ## **8.8 Security Best Practices** ### **8.8.1 Always Escape Output** ```html
    ##{$userComment}#
    ##{htmlspecialchars($userComment)}#
    ##{raw:$adminGeneratedHtml}#
    ``` ### **8.8.2 Validate Before Use** ```html #{if isset($userInput) && is_string($userInput) }#

    Input: ##{$userInput}#

    #{else}#

    No valid input provided

    #{endif}# #{ $userId = (int)$inputUserId; }#

    User ID: ##{$userId}#

    ``` ## **8.9 Debugging Expression Tags** ### **8.9.1 Enable Debug Output** ```html
    Variable: ##{$var}# Type: ##{gettype($var)}# Is Set: ##{isset($var) ? 'Yes' : 'No'}# Is Empty: ##{empty($var) ? 'Yes' : 'No'}# ``` ### **8.9.2 Common Errors and Solutions** **Error: "Undefined variable"** ```html

    ##{$undefinedVar}#

    #{if isset($undefinedVar) }#

    ##{$undefinedVar}#

    #{else}#

    Variable not available

    #{endif}# ``` **Error: "Function not allowed"** ```html

    ##{exec('ls')}#

    ##{date('Y-m-d')}#

    ##{$parentgate->safeFunction()}#

    ``` **Error: "Syntax error in expression"** ```html

    ##{ if true { echo "yes"; } }#

    ##{true ? "yes" : "no"}#

    #{if true }#

    yes

    #{endif}# ``` ### **8.9.3 Development Tools** Add debugging helpers to your App: ```php // In Gate base class or helper public function debugExpression($expression, $label = '') { if ($this->debug->debugmode > 1) { $result = eval("return $expression;"); error_log("Expression Debug [$label]: $expression = " . print_r($result, true)); return $result; } return null; } ``` **Usage:** ```html #{ $debugResult = $parentgate->debugExpression('2+2', 'test'); }#

    Debug: ##{$debugResult}#

    ``` ## **8.10 Real-World Examples** ### **8.10.1 E-commerce Product Display** ```html #{ $product = $parentgate->currentProduct; $user = $parentgate->currentUser; $inCart = $parentgate->isInCart($product->id); $inStock = $product->stock > 0; $onSale = $product->sale_price < $product->regular_price; $discountPercent = $onSale ? round((1 - $product->sale_price / $product->regular_price) * 100) : 0; }#
    #{if $onSale }#
    Save ##{$discountPercent}#%
    #{endif}# ##{htmlspecialchars($product->name)}#
    ##{$product->name}#
    #{if $onSale }# $##{number_format($product->regular_price, 2)}# $##{number_format($product->sale_price, 2)}# #{else}# $##{number_format($product->regular_price, 2)}# #{endif}#
    #{if $inStock }# In Stock (##{$product->stock}# available) #{else}# Out of Stock #{endif}#
    #{if $inStock }# #{else}# #{endif}#
    ⭐ ##{number_format($product->rating, 1)}# (##{$product->review_count}# reviews) | 🔥 ##{$product->sold_count}# sold
    ``` ## **8.11 Chapter Summary** Expression tags provide a powerful yet safe way to add dynamic logic to SartajPHP FrontFiles: 1. **Two Tag Types:** - `##{ }#` - Output tags (display results) - `#{ }#` - Silent tags (execute without output) 2. **Supported Syntax:** - Variable assignment and arithmetic - String operations and concatenation - If/elseif/else conditional statements - Ternary operators (including nested) - Limited function calls (safe functions only) - Comparison and logical operators 3. **Key Limitations (for safety):** - No function or class definitions - No access to superglobals (`$_GET`, `$_POST`, etc.) - No arbitrary object creation - Limited function whitelist 4. **Execution Timing:** - Normal HTML: After all Components render - Fusion attributes: Depends on prefix (`fui-*`, `fur-*`, `fun-*`) - Cannot affect Component rendering from normal HTML expressions 5. **Best Practices:** - Keep expressions simple; complex logic belongs in App - Always escape output (default behavior) - Pre-calculate expensive operations in App - Use Fusion attributes when you need to affect Components - Validate data before using in expressions Expression tags strike the perfect balance between template flexibility and application security. They provide enough power for sophisticated presentation logic while preventing the common security pitfalls of allowing arbitrary PHP in templates. In the next chapter, we'll explore Multi-Application Architecture—how to build complex systems by combining multiple focused applications within a single SartajPHP project. --- # **Chapter 9: Expression Tags - Safe Dynamic Content with Gate Integration** ## **9.1 The Strict Expression Tag Philosophy in SartajPHP v5** SartajPHP version 5 introduces important restrictions on expression tag usage to ensure security, performance, and maintainability. The key principle is: **Expression tags are for simple output, not complex logic.** ### **9.1.1 What You CANNOT Do in Expression Tags v5** 1. **No Function Definitions:** ```html #{ function formatPrice($price) { return '$' . number_format($price, 2); } }# ``` 2. **No Anonymous Functions via addProp:** ```php // ❌ NOT ALLOWED in v5 $this->frtMain->addProp('formatPrice', function($price) { return '$' . number_format($price, 2); }); ``` 3. **No Complex Loops in Expression Tags:** ```html #{ $output = ''; foreach ($products as $product) { $output .= "
    {$product->name}
    "; } echo $output; }# ``` 4. **No Component Generation from Expression Tags:** ```html ##{ // Cannot create or modify Components $component = new \Sphp\Comp\Form\TextField(); echo $component->render(); }# ``` ### **9.1.2 What You CAN Do in Expression Tags v5** 1. **Simple Variable Output:** ```html

    Welcome, ##{$userName}#!

    Price: $##{$productPrice}#

    ``` 2. **Basic Ternary Operations:** ```html

    Status: ##{$isActive ? 'Online' : 'Offline'}#

    ``` 3. **Simple String Concatenation:** ```html

    ##{'Hello ' . $firstName . ' ' . $lastName}#

    ``` 4. **Basic Arithmetic:** ```html

    Total: $##{$price * $quantity}#

    Discount: ##{$discountPercent}#%

    ``` 5. **Call Gate Methods for Complex Logic:** ```html ##{raw:$parentgate->generateProductGrid($products)}# ##{raw:$parentgate->formatCurrency($amount)}# ``` ## **9.2 The Correct Pattern: Gate Methods for Complex Logic** The proper way to handle complex presentation logic in SartajPHP v5 is to create methods in your Application class and call them from expression tags. ### **9.2.1 Basic Pattern** **In App:** ```php class My extends \Sphp\tools\BasicGate { public function generateProductList($products) { $html = '
    '; foreach ($products as $product) { $html .= $this->generateProductItem($product); } $html .= '
    '; return $html; } private function generateProductItem($product) { return sprintf( '

    %s

    %s

    $%.2f

    ', htmlspecialchars($product['name']), htmlspecialchars($product['description']), $product['price'] ); } } ``` **In FrontFile:** ```html
    ##{raw:$parentgate->generateProductList($products)}#
    ``` ### **9.2.2 Using Holder Tags with Gate Methods** A cleaner approach is to use holder tags that call Gate methods: **In App:** ```php class My extends \Sphp\tools\BasicGate { // This method will be called by holder tag public function onholder($element) { if ($element->getAttribute('data-type') === 'product-grid') { $products = $this->getProducts(); $html = $this->generateProductGrid($products); $element->setInnerHTML($html); } } private function generateProductGrid($products) { $html = '
    '; foreach ($products as $product) { $html .= $this->generateProductCard($product); } $html .= '
    '; return $html; } private function generateProductCard($product) { $onSale = $product['sale_price'] < $product['regular_price']; $discount = $onSale ? round((1 - $product['sale_price'] / $product['regular_price']) * 100) : 0; return sprintf( '
    %s
    %s
    %s
    ', $onSale ? 'Save ' . $discount . '%' : '', htmlspecialchars($product['name']), $onSale ? '$' . number_format($product['regular_price'], 2) . ' ' . '$' . number_format($product['sale_price'], 2) . '' : '$' . number_format($product['regular_price'], 2) . '' ); } } ``` **In FrontFile:** ```html
    ``` ## **9.3 Using Components for Loops Instead of Expression Tags** When you need to repeat content, use Components instead of expression tag loops: ### **9.3.1 ForLoop Component** **In FrontFile:** ```html

    $

    ``` **In App:** ```php public function onready() { $products = $this->getProducts(); $loop = $this->frtMain->getComponent('productLoop'); // Set data for the loop $loop->fu_setData($products); } ``` ### **9.3.2 DataGrid Component** For tabular data: **In FrontFile:** ```html
    ``` ## **9.4 Using Code Blocks for Layout Logic** Code blocks (`runcb="true"`) are perfect for layout transformations without complex expression logic: ### **9.4.1 Define Code Blocks** **In masters/sphpcodeblock.php:** ```php SphpCodeBlock::addCodeBlock('product_card', function($element, $args, $context) { // Extract product data from args $product = $args[0] ?? []; if (empty($product)) { return; } // Generate card HTML $html = '
    '; if (!empty($product['image'])) { $html .= '' . htmlspecialchars($product['name']) . ''; } $html .= '
    '; $html .= '
    ' . htmlspecialchars($product['name']) . '
    '; if (!empty($product['description'])) { $html .= '

    ' . htmlspecialchars($product['description']) . '

    '; } $html .= '

    $' . number_format($product['price'], 2) . '

    '; $html .= '
    '; $element->setInnerHTML($html); }); SphpCodeBlock::addCodeBlock('user_badge', function($element, $args, $context) { $user = $args[0] ?? []; $size = $args[1] ?? 'medium'; if (empty($user)) { return; } $sizeClass = [ 'small' => 'badge-sm', 'medium' => 'badge-md', 'large' => 'badge-lg' ][$size] ?? 'badge-md'; $roleClass = [ 'admin' => 'badge-danger', 'moderator' => 'badge-warning', 'user' => 'badge-primary', 'guest' => 'badge-secondary' ][$user['role']] ?? 'badge-secondary'; $element->appendAttribute('class', 'user-badge ' . $sizeClass . ' ' . $roleClass); $element->setInnerHTML(htmlspecialchars($user['name'])); }); ``` ### **9.4.2 Use Code Blocks in FrontFile** ```html
    ``` **In App:** ```php public function onholder($element) { if ($element->getAttribute('data-type') === 'product_cards') { $products = $this->getProducts(); $html = ''; foreach ($products as $product) { // Pass product data to code block via data attribute $html .= '
    '; } $element->setInnerHTML($html); } } ``` ## **9.5 Best Practices for SartajPHP v5 Expression Tags** ### **9.5.1 Do's and Don'ts** **✅ DO:** - Use simple variable output: `##{$variable}#` - Use basic ternary operators: `##{$condition ? 'Yes' : 'No'}#` - Use string concatenation: `##{'Hello ' . $name}#` - Use arithmetic: `##{$price * $quantity}#` - Call Gate methods for complex logic: `##{raw:$parentgate->method()}#` **❌ DON'T:** - Define functions in expression tags - Create complex loops in expression tags - Pass anonymous functions via `addProp()` - Generate Components from expression tags - Include business logic in expression tags ### **9.5.2 Recommended Architecture** ``` FrontFile (View Layer) ├── Simple Expression Tags (display only) ├── Holder Tags (for dynamic content) ├── Components (for reusable UI elements) └── Code Blocks (for layout transformations) Gate Class (Logic Layer) ├── Methods for complex HTML generation ├── Data preparation and processing ├── Component configuration └── Business logic Components (UI Layer) ├── ForLoop, DataGrid for repeating content ├── Custom Components for specialized UI └── Built-in Components for forms, etc. ``` ### **9.5.3 Performance Tips** 1. **Pre-generate HTML in App:** Complex HTML should be generated once in Gate methods 2. **Use Components for repeated content:** Components are optimized for loops 3. **Cache expensive operations:** Cache generated HTML in Gate if appropriate 4. **Minimize expression tag complexity:** Keep expressions simple and fast ## **9.6 Chapter Summary** SartajPHP v5 expression tags follow a strict but sensible philosophy: 1. **Safety First:** No arbitrary code execution in templates 2. **Separation of Concerns:** Presentation logic in App, not in templates 3. **Performance Focus:** Pre-generated HTML for complex content 4. **Maintainability:** Clean, readable templates without embedded logic **Key Takeaways:** - Expression tags are for **simple output only** - Complex logic belongs in **Gate methods** - Use `##{raw:$parentgate->method()}#` for complex HTML - Use **Components** for repeating content (not expression tag loops) - Use **Code Blocks** for layout transformations - Keep templates clean and focused on presentation This approach ensures your SartajPHP applications are secure, maintainable, and performant. While it requires more upfront planning, it pays dividends in reduced bugs, better security, and easier maintenance. --- # **Chapter 10: SartajPHP Permission System Architecture** ## SartajPHP Permission System Architecture Security in SartajPHP is not an afterthought, a plugin, or a middleware layer. It is a **first-class application concern**, designed to work naturally with the App, Event, and FrontFile lifecycle. This chapter explains **how the SartajPHP Permission System actually works**, why it is designed this way, and how developers must use it correctly. --- ## 10.1 Authentication vs Authorization (Critical Distinction) SartajPHP clearly separates **authentication** from **authorization**. | Layer | Purpose | Question Answered | | -------------------------- | ---------- | ------------------------------- | | Authentication | Identity | Who is the user? | | Authorization (Permission) | Capability | What is the user allowed to do? | This separation allows: * Simple apps without permissions * Advanced enterprise apps with dynamic permissions * Runtime permission updates without code changes --- ## 10.2 Authentication Layer (Recap) Authentication is handled primarily by the **Signin App** and project configuration (`comp.php`). Authentication determines: * Is the user logged in? * What role does the user belong to? Typical roles: * `GUEST` * `MEMBER` * `ADMIN` Apps can restrict access using: ```php $this->getAuthenticate("GUEST,MEMBER,ADMIN"); ``` This check: * Controls **who may access the App** * Does **not** control what actions are allowed inside the App That responsibility belongs to the Permission system. --- ## 10.3 Permission System Philosophy The SartajPHP Permission System is: * **App-centric** * **Event-aware** * **Database-driven** * **Explicit, not implicit** Permissions are **never inferred automatically**. If a developer does not check a permission, **no permission is enforced**. This design: * Avoids hidden behavior * Prevents unexpected access denial * Keeps Apps predictable and debuggable --- ## 10.4 Permission Identifier Format Permissions in SartajPHP follow a strict naming rule: ``` - ``` Example: ``` mebProfile-view mebProfile-add mebProfile-delete ``` Important rules: * Developers only use the **short permission name** (`view`, `add`, `delete`) * The framework automatically prefixes the Gate name * Permission names are **case-sensitive** --- ## 10.5 Registering Permissions for an App Permissions are declared **at Gate registration time**, usually inside `reg.php`. Example: ```php uuregisterGate( "mebProfile", ".../uumebProfile.gate.php", "", "Member Profile", [ ["view", "View Profile"], ["add", "Add Record"], ["update", "Update Record"], ["delete", "Delete Record"] ] ); ``` ### What This Does * Registers the App * Declares available permissions * Makes permissions visible to: * Permission Profile App * Menu system * UI permission assignment No database records are created at this stage. This is **permission definition**, not permission assignment. --- ## 10.6 Where Permissions Are Stored Permissions are stored **per profile**, not per user. Typical flow: * User → assigned to a profile * Profile → assigned permissions * User inherits permissions from profile This design: * Simplifies permission management * Prevents permission duplication * Allows bulk permission changes --- ## 10.7 Permission Enforcement Inside an App Permissions are enforced **explicitly** inside Gate logic. ### Event-Level Enforcement ```php $this->page->getAuthenticatePerm("view"); ``` If permission is missing: * Execution stops * Gate falls back to safe behavior * Unauthorized action is blocked --- ### Conditional Permission Checks ```php if ($this->page->hasPermission("add")) { // allow action } ``` This is commonly used for: * Showing or hiding buttons * Enabling or disabling features * Conditional logic in FrontFiles --- ## 10.8 Role of `PermisApp` (Parent App) `PermisApp` is a **permission-aware base App**. When an Gate extends `PermisApp`, it automatically gains: * Permission checking helpers * Consistent permission lifecycle * Standardized enforcement behavior Typical usage: ```php class mebProfile extends PermisGate { ... } ``` ### Why `PermisApp` Exists * Prevents duplicated permission logic * Centralizes security behavior * Ensures consistency across Apps * Integrates seamlessly with Menu system Using `PermisApp` is **strongly recommended** for all secure Apps. --- ## 10.9 Permission Enforcement vs Menu Visibility A critical rule in SartajPHP: > **Menu visibility does NOT equal permission enforcement** Even if: * A menu item is hidden * A link is not visible The Gate **must still check permissions**. Menus are a **UI convenience**, not a security boundary. --- ## 10.10 Menu System and Permissions Menus in SartajPHP are **permission-aware**. Example: ```php $this->sphp_api->addMenuLink("Users",getGateURL('mebProfile'),"fa fa-users","User",false,"mebProfile-view"); ``` This means: * Menu link appears only if: * User role matches * User has the `mebProfile-view` permission Menu filtering is done: * At render time * Based on logged-in user permissions This prevents: * Confusing UI * Broken navigation * Unauthorized access attempts --- ## 10.11 Why Menu Permissions Depend on Gate Permissions Menus do **not define permissions**. They only **reference existing Gate permissions**. This ensures: * Single source of truth * No permission duplication * App-driven security model If an Gate does not register permissions, the menu cannot reference them. --- ## 10.12 Permission Checks in Front Files FrontFiles can conditionally render elements using server-bound logic. Typical pattern: * Permission check in App * Flag passed to FrontFile * Conditional rendering using `runat="server"` FrontFiles themselves contain: * No direct permission logic * No SQL * No authentication code They are **presentation-only**, by design. --- ## 10.13 Admin Privileges The `ADMIN` role: * Automatically bypasses permission checks * Has access to all Apps and events * Is intended for system administrators only This behavior: * Is enforced centrally * Should never be overridden casually --- ## 10.14 Common Permission Mistakes (Avoid These) * Assuming menu hiding is security * Forgetting to enforce permission in Gate logic * Using permissions without registering them * Hardcoding permission strings incorrectly * Skipping `PermisApp` for secure Apps --- ## 10.15 Summary The SartajPHP Permission System is: * Explicit * Predictable * App-centric * Database-driven * UI-aware but not UI-dependent Correct usage requires: * Proper permission registration * Explicit permission checks * Consistent use of `PermisApp` * Clear separation of Gate logic and FrontFiles --- ## 10.16 What Comes Next In **Chapter 11**, we will cover: * Required database tables * Mandatory fields (`id`, `parentid`, `permission_id`) * SQL schema rules * Why `id` is critical for components * How permissions are stored and resolved internally # **Chapter 11: PermisGate Architecture & Database Design** This chapter explains **PermisApp**, its role in SartajPHP, and the **database structure required** to use it effectively. PermisGate is **not mandatory**. It is a **shared parent Application** provided by SartajPHP to **reduce repetitive work** when building authentication-, user-, and permission-based systems. --- ## 11.1 What PermisGate Really Is (And Is Not) PermisApp: - ✅ Is a **helper parent App** - ✅ Is aligned with **Signin / Signup / User Management Apps** - ✅ Provides **prebuilt CRUD behaviors** - ✅ Enforces **permission-aware operations** PermisGate is **NOT**: - ❌ The core of SartajPHP - ❌ Required to build permission-based Apps - ❌ A replacement for `BasicGate` - ❌ A restriction on developer freedom > **Important:** > The true root Application class in SartajPHP is always `BasicGate`. > `PermisApp` simply extends it with shared logic. --- ## 11.2 Why PermisGate Exists In real-world projects, developers repeatedly need: - Signin / logout logic - User record ownership - Parent-child user relationships - Profile & permission handling - Standard insert / update / delete flows - Permission validation before database actions Writing this **again and again** leads to: - Code duplication - Inconsistent permission checks - Security bugs PermisGate solves this by providing a **common, reusable foundation**. --- ## 11.3 When You Should Use PermisApp Use PermisGate when your project includes: - Login system - User hierarchy - Profiles & permissions - Admin / staff / user roles - Multi-user data ownership - Large or growing applications Do **not** use PermisGate when: - Your app is small - No authentication is needed - No permission logic is required SartajPHP leaves the decision **entirely to the developer**. --- ## 11.4 Core Database Philosophy Behind PermisApp PermisGate assumes that **every record belongs to a user context**. That context is defined using **mandatory structural fields**. These fields allow PermisGate to: - Track ownership - Enforce permissions - Support hierarchical users - Enable multi-company separation - Provide recovery and debugging --- ## 11.5 Mandatory Fields for PermisApp-Compatible Tables Any database table used with PermisGate **must include** the following fields. ### 11.5.1 `id` — Record Identity (Required) ```sql id INT PRIMARY KEY AUTO_INCREMENT ```` * Unique identifier of the record * Used by: * Page events * Components * Pagination * Edit / delete operations * Must be numeric * Must be unique This field is **non-negotiable**. --- ### 11.5.2 `userid` — Owner User Record ID ```sql userid INT NOT NULL ``` * Stores the **RecID of the logged-in user** * Retrieved from Signin App * Used to: * Restrict record access * Filter list results * Enforce ownership rules PermisGate automatically fills this field during insert. --- ### 11.5.3 `parentid` — Parent User Record ID ```sql parentid INT DEFAULT 0 ``` * Stores the **RecID of the parent user** * Used for: * Admin → staff → user hierarchies * Delegated permissions * Group-based visibility This enables **multi-level user control** without complex joins. --- ### 11.5.4 `spcmpid` — Company / Project Identifier ```sql spcmpid VARCHAR(50) ``` * Text-based company or project identifier * No spaces allowed * Defined in `comp.php` * Optional for small projects * Essential for large or multi-tenant systems Use case: * Multiple companies share one website * Data must remain logically separated * Same user structure, different company data PermisGate filters records by `spcmpid` automatically when enabled. --- ### 11.5.5 `submit_timestamp` — Record Creation Time ```sql submit_timestamp DATETIME ``` * Automatically set during insert * Used for: * Debugging * Logging * Auditing * Recycle Gate integration --- ### 11.5.6 `update_timestamp` — Record Update Time ```sql update_timestamp DATETIME ``` * Updated on each modification * Helps track: * Who changed what * When data was modified * Failed update attempts --- ## 11.6 Canonical PermisApp-Compatible Table Example ```sql CREATE TABLE meb_profile ( id INT PRIMARY KEY AUTO_INCREMENT, userid INT NOT NULL, parentid INT DEFAULT 0, spcmpid VARCHAR(50), profile_name VARCHAR(100), status TINYINT DEFAULT 1, submit_timestamp DATETIME, update_timestamp DATETIME ); ``` This structure allows: * Secure CRUD * Permission enforcement * Multi-company isolation * Recovery support --- ## 11.7 Built-in CRUD Support Provided by PermisApp PermisGate already implements: * View logic * Insert logic * Update logic * Delete logic * Permission checks before execution As a result: * You **do not rewrite** CRUD code * You **do not duplicate permission checks** * You **only focus on business logic** --- ## 11.8 Overriding PermisGate Behavior (Advanced) PermisGate is **fully overridable**. If you need custom behavior: ```php class mebProfile extends PermisGate { protected function beforeInsert(&$data) { // custom validation } protected function afterUpdate($id) { // logging or notifications } } ``` This follows pure OOP principles: * Extend * Override * Customize only what you need --- ## 11.9 Recycle Gate & Failure Recovery The timestamp fields enable integration with a **Recycle App**: * Deleted records can be stored instead of destroyed * Failed SQL inserts can be logged * Accidental deletions can be restored This is especially useful in: * Financial systems * HR systems * Enterprise projects --- ## 11.10 Important Clarification (Very Important) > PermisGate is **optional**. If you: * Understand SartajPHP API * Know how to implement permissions manually * Want a custom architecture You can: * Extend `BasicGate` * Build your own permission-aware system SartajPHP **never restricts you**. PermisGate exists to **save time**, not to enforce rules. --- ## 11.11 Summary * `BasicGate` is the true root App * `PermisApp` is a productivity layer * Database compatibility is required *only if you use PermisApp* * Required fields enable ownership, hierarchy, and safety * Developers retain full freedom and control --- ## 11.12 What Comes Next In **Chapter 12**, we will cover: * How Signin Gate feeds PermisApp * Login lifecycle * Session identity * How permissions are resolved at runtime * Secure PageEvent execution --- # **Chapter 12: Signin, Session Identity & Permission Resolution Flow** This chapter explains **how authentication, session identity, and permissions actually work** in SartajPHP. It connects: - Signin App - Session lifecycle - Permission checks - PageEvents - Optional `PermisApp` behavior Understanding this chapter is critical before building **secure, real-world applications**. --- ## 12.1 Authentication Is an Application Responsibility In SartajPHP: - There is **no global auth middleware** - There is **no automatic login enforcement** - There is **no hidden session magic** Authentication is handled by: - A dedicated **Signin Application** - Explicit API calls - Clear developer intent This keeps SartajPHP: - Predictable - Flexible - Transparent --- ## 12.2 The Role of the Signin App The Signin Gate is responsible for: - Validating credentials - Loading user record - Initializing session identity - Assigning profile information - Making user context available to other Apps Once Signin completes successfully: - The user is considered authenticated - Other Apps can safely query identity --- ## 12.3 User Identity in SartajPHP Sessions After successful login, SartajPHP stores **identity data**, not raw credentials. Typical session values include: - User record ID (`userid`) - Parent user ID (`parentid`) - Profile ID - Company ID (`spcmpid`) - Login state flag These values are **read-only** during request processing. Apps do not modify session identity directly. --- ## 12.4 How Apps Access Logged-in User Information Applications retrieve user context using the API: ```php SphpBase::page()->getAuthenticate(); ```` This call: * Verifies login state * Stops execution if user is not logged in * Redirects or blocks based on configuration For Apps extending `PermisApp`, this check is often **automatic**. --- ## 12.5 Authentication vs Authorization (Critical Difference) SartajPHP strictly separates: ### Authentication > “Who are you?” Handled by: * Signin App * Session identity ### Authorization > “What are you allowed to do?” Handled by: * Permissions * Profiles * Explicit permission checks This separation prevents: * Accidental privilege escalation * Mixed logic * Security bugs --- ## 12.6 Permission Resolution Flow When a permission check occurs: 1. AppGate is identified 2. Current PageEvent is resolved 3. User profile is loaded from session 4. Permission string is evaluated 5. Access is allowed or denied This process is: * Deterministic * Explicit * Fast (cached per request) --- ## 12.7 Permission Check API The canonical permission check method: ```php $this->page->getAuthenticatePerm("view"); ``` This means: * User must be logged in * User must have permission for: ``` -view ``` If permission fails: * PageEvent stops immediately * No database operation executes * No FrontFile renders --- ## 12.8 How PermisGate Uses Authentication If an Gate extends `PermisApp`: * Authentication is enforced automatically * User identity fields (`userid`, `parentid`, `spcmpid`) are injected * CRUD operations are permission-aware by default This reduces boilerplate while keeping control in the App. --- Got it 👍 — here is a **clean rewrite of Section 12.9 only**, fully aligned with how **SartajPHP actually works**, correcting the earlier lifecycle/security ordering and clearly separating **Security Layer vs Gate Layer**. You can directly replace **12.9** with this. --- ## 12.9 Security Layer vs Gate Layer In SartajPHP, **security is NOT part of the Gate lifecycle itself**. It is a **separate layer** that executes **before Gate logic is allowed to run**. Understanding this separation is very important. --- ### 12.9.1 Request Translation (Framework Layer) When a browser sends a request: 1. Browser URL is translated into: * **AppGate** * **Event** * **Event Parameter** 2. SartajPHP resolves the AppGate to the corresponding `.gate.php` file. 3. The Gate class is loaded into memory. At this stage: * No Gate code is executed * No security is checked yet --- ### 12.9.2 Gate Lifecycle Resolution (Gate Layer) After resolving the App: 4. SartajPHP prepares to execute the **Gate Lifecycle Events** * `onstart` * `onfrontinit` * `onfrontprocess` * `onready` * `onrun` * `onrender` However… --- ### 12.9.3 Security Check Happens First (Security Layer) Before **any Gate logic is allowed to continue**, SartajPHP performs **security validation at the top of `onstart`**. This means: * Security checks are evaluated **before PageEvents** * If security fails, execution **stops immediately** * No FrontFile renders * No database operation runs Security is **opt-in**, not automatic. --- ### 12.9.4 Role-Based Authentication To require a specific role, developers explicitly declare it: ```php $this->getAuthenticate("ADMIN"); ``` This is equivalent to: ```php $this->page->Authenticate("ADMIN"); ``` What this does: * Checks the user role set by `setSession()` in `uusignin.gate.php` * Verifies whether the logged-in user is `ADMIN` * Blocks access if role does not match If no authentication method is declared: * The Gate is **public** * Anyone can access it (e.g. `Index.gate.php` or website home page) --- ### 12.9.5 PageEvents Do NOT Check Security Automatically Important rule: > **PageEvents never enforce security by default** That means: * `page_new` * `page_submit` * `page_event_*` …will all execute **unless security is explicitly defined**. Security must always be declared using: * `getAuthenticate()` * `getAuthenticatePerm()` --- ### 12.9.6 Session Security (`getSesSecurity`) ```php $this->getSesSecurity(); ``` This check: * Prevents access with **expired or invalid session IDs** * Blocks replayed or invalid URLs It is: * Rarely used * Optional * Mostly required in high-security systems --- ### 12.9.7 Permission + Role Authentication For role **and** permission checks together: ```php $this->page->getAuthenticatePerm("view"); ``` This performs: * Role validation (GUEST / MEMBER / ADMIN) * Permission validation (`AppGate-view`) You can authorize multiple roles or permissions: ```php $this->page->getAuthenticatePerm("ADMIN,MEMBER"); $this->page->getAuthenticatePerm("view,add"); ``` Comma-separated values allow flexible access control. --- ### 12.9.8 Key Design Principle SartajPHP never assumes security. * Developers decide **who can access** * Developers decide **what is protected** * Developers can mix: * Public Apps * Role-based Apps * Permission-based Apps * Hybrid Apps This design gives **full freedom**, not restrictions. --- ### 12.9.9 Summary * Security layer is separate from Gate logic * URL → AppGate → Gate resolution happens first * Security is checked at the top of `onstart` * PageEvents never auto-check security * Roles come from `uusignin.gate.php` * Permissions are optional and explicit * Nothing is enforced unless the developer says so --- ## 12.10 Login-Aware FrontFile Behavior FrontFiles do **not** check authentication. Instead: * Apps decide what FrontFile to render * Apps decide whether rendering is allowed * Components may be conditionally visible This keeps FrontFiles: * Clean * UI-focused * Security-agnostic --- ## 12.11 Logout Flow Logout is handled by: * Clearing session identity * Destroying login state * Redirecting to Signin App After logout: * All Apps requiring authentication fail immediately * No permission checks pass * User context is unavailable --- ## 12.12 Multi-Company Identity (`spcmpid`) For large projects: * `spcmpid` is loaded during login * Stored in session * Used by PermisGate to filter records This enables: * Multi-tenant architecture * Shared codebase * Isolated data per company Small projects may ignore this field safely. --- ## 12.13 Custom Authentication Systems SartajPHP allows you to: * Replace Signin App * Extend it * Create multiple login systems * Integrate third-party auth As long as: * Session identity is set correctly * API expectations are respected There is **no lock-in**. --- ## 12.14 Common Mistakes to Avoid ❌ Checking permissions in FrontFiles ❌ Running SQL before permission checks ❌ Mixing login logic into Apps ❌ Hardcoding session values ❌ Assuming PermisGate is mandatory --- ## 12.15 Summary * Signin Gate handles authentication * Session holds identity, not logic * Permissions are resolved per AppGate + Event * PermisGate automates common patterns * Developers retain full control --- ## 12.16 What Comes Next In **Chapter 13**, we will cover: * Profile & Permission management Apps * Permission assignment UI * Dynamic permission filtering * Why users cannot assign permissions they don’t have * Real-world permission workflows --- # **Chapter 13: Profile & Permission Management System in SartajPHP** ## Profile & Permission Management System in SartajPHP This chapter explains **how profiles and permissions are created, stored, assigned, and enforced** in SartajPHP, and how Apps like `mebProfile` and `mebProfilePermission` work together to provide a flexible authorization system. --- ## 13.1 Why SartajPHP Uses Profile-Based Permissions SartajPHP separates **authentication** from **authorization**: * **Authentication** → Who the user is * **Authorization** → What the user can do Instead of assigning permissions directly to users, SartajPHP assigns permissions to **profiles**, and users are linked to profiles. This design provides: * Easier permission management * Reusable permission sets * Safer delegation * Scalable multi-user systems --- ## 13.2 Core Database Tables Involved The permission system relies on two core tables: ### 1. `member` Table Represents users. Key fields used by the permission system: * `id` → User Record ID (Primary Key) * `profile_id` → Linked profile * `userid` → Creator user ID * `parentid` → Parent user ID * `spcmpid` → Company identifier * `usertype` → `ADMIN` or `MEMBER` --- ### 2. `profile_permission` Table Represents profiles and their assigned permissions. Important fields: * `id` → Profile ID (Primary Key) * `profile_name` → Human-readable profile name * `permission_id` → Permission list (CSV or serialized) * `userid`, `parentid`, `spcmpid` * `submit_timestamp`, `update_timestamp` Each record represents **one profile**. --- ## 13.3 How Permissions Are Defined (Registration Time) Permissions are declared **when an Gate is registered**, not in the database. Example: ```php uuregisterGate( "mebProfile", "{$slibpath}/apps/permis/uumebProfile.gate.php", "", "Profile", [ ["view","Profile View"], ["add","Add Record"], ["delete","Delete Record"] ] ); ``` Meaning: * `"view"` → Internal permission name (developer-facing) * `"Profile View"` → Display label (user-facing) Internally, SartajPHP expands this into: ``` mebProfile-view mebProfile-add mebProfile-delete ``` These expanded permission keys are what the framework actually checks. --- ## 13.4 Profile Creation Workflow (`uumebProfile.gate.php`) The Profile Gate allows administrators to: * Create new profiles * Edit existing profiles * Assign permissions Typical flow: 1. Admin opens Profile App 2. Uses **list FrontFile** to browse profiles 3. Uses **edit FrontFile** to create or update profiles 4. Permissions are saved as text in `permission_id` The Gate itself does not need to understand permissions deeply — it only stores them. --- ## 13.5 Permission Assignment UI (`uumebProfilePermission.gate.php`) The Permission Gate provides a UI that: * Reads all registered Apps * Reads permissions declared by each App * Displays them as grouped checkboxes * Prevents users from assigning permissions they do not own This ensures **permission inheritance safety**. --- ## 13.6 Permission Inheritance Rule (Very Important) A user can **only assign permissions that they already have**. This rule prevents: * Privilege escalation * Unauthorized permission granting If an admin does not have: ``` mebProfile-delete ``` They cannot assign that permission to any profile. This rule is enforced at runtime using: ```php SphpBase::sphp_permissions()->hasPermission() ``` --- ## 13.7 How Permissions Are Stored Permissions are stored as a **text list** inside: ```sql profile_permission.permission_id ``` Example value: ``` mebProfile-view,mebProfile-add,mebProfilePermission-view ``` Why text storage? * Database-agnostic * Easy migration * No join overhead * Faster permission checks Parsing is handled internally by SartajPHP. --- ## 13.8 Permission Evaluation at Runtime When a request is processed: 1. User logs in 2. Profile ID is loaded from `member.profile_id` 3. Permissions are loaded from `profile_permission.permission_id` 4. Permissions are cached in session: ```php $this->Client->session('lstpermis', $row['permission_id']); ``` 5. Permission checks are executed using: ```php $this->page->hasPermission("view"); ``` The Gate does **not** need to know: * Which profile the user has * Where permissions came from * How they are stored --- ## 13.9 Permission Checks Inside Apps Inside an App, permission checks are explicit: ```php if ($this->page->hasPermission("add")) { // allow insert } ``` Or globally at Gate entry: ```php $this->page->getAuthenticatePerm("view"); ``` This ensures: * No hidden logic * No magic behavior * Full developer control --- ## 13.10 Why `PermisApp` Makes This Easier `PermisApp` is a **helper parent class**, not a framework requirement. It provides: * Standard CRUD flow * Permission checks for common operations * Automatic DB binding * Reduced boilerplate But: * You can build permission-based Apps without it * The real parent class is always `BasicGate` --- ## 13.11 Common Mistakes to Avoid 1. Assuming permissions auto-apply → They don’t. You must check them. 2. Naming Apps poorly → AppGate name defines permission namespace. 3. Mixing role logic with permission logic → Keep them separate. 4. Forgetting `id` as primary key → Many components rely on it. --- ## 13.12 Design Philosophy SartajPHP treats permissions as: * **Data-driven** * **Developer-defined** * **User-assignable** * **Framework-enforced** Nothing is hidden. Nothing is forced. Everything is explicit. That is why SartajPHP can scale from: * Small admin panels * To multi-company SaaS platforms --- ### End of Chapter 13 --- # **Chapter 14: Menu System & Permission-Aware Navigation in SartajPHP** ## Menu System & Permission-Aware Navigation in SartajPHP This chapter explains **how SartajPHP manages menus with permissions**, and how **menu rendering is separated from menu definition**. The framework provides **menu data**, while **design and HTML generation are fully customizable**. --- ## 14.1 Menu System Philosophy SartajPHP follows a clear principle: > **Framework provides menu data, not design.** This means: * Framework manages **menu list, hierarchy, permissions** * Renderer (like BootstrapMenu) decides **HTML output** * Developers can replace renderer without changing Gate logic This design keeps **UI flexible** and **logic centralized**. --- ## 14.2 Menu Data vs Menu Renderer There are two distinct layers: ### 1. Menu Data Layer (Framework) Handled by `Sphp\core\SphpAPI` Responsibilities: * Store menus * Store menu links * Apply permission rules * Provide structured menu lists ### 2. Menu Renderer Layer Handled by classes like `BootstrapMenu` Responsibilities: * Convert menu lists to HTML * Apply CSS framework (Bootstrap) * Handle dropdowns, icons, keyboard shortcuts * Inject JS & CSS when required --- ## 14.3 Menu API Overview The framework exposes **menu APIs** through `SphpAPI`. ### addMenu() Creates a menu container (can have sub-menus and links). Key parameters: * `$text` → Menu label * `$link` → Optional URL * `$parent` → Parent menu (default `root`) * `$roles` → Role or permission filter Menus can exist **without links**, acting as dropdown containers. --- ### addMenuLink() Adds an actual clickable item under a menu. Key parameters: * `$text` → Link label * `$link` → URL or JavaScript action * `$parent` → Menu name * `$roles` → Role or permission string Permissions can be: * Login types (`ADMIN`, `MEMBER`) * Permission keys (`mebProfile-view`) * Or empty (public) --- ## 14.4 Permission Filtering in Menu System Permission filtering is **not handled in the menu file**. It is handled **inside the renderer** using framework permission services. Example logic used internally: * If role/permission is empty → show menu * Else → check via `sphp_permissions()->isPermission()` This ensures: * Unauthorized users never see restricted menus * No manual checks required in HTML * Menu adapts dynamically per user --- ## 14.5 BootstrapMenu: A Reference Renderer `BootstrapMenu` is a **menu renderer**, not a framework requirement. Its responsibilities include: * Building Bootstrap 4 / 5 compatible navigation * Supporting nested menus * Supporting AJAX links * Supporting keyboard shortcuts * Highlighting active menu automatically Important: > **All permission logic is respected automatically**, because renderer reads filtered menu lists. --- ## 14.6 Keyboard Shortcut Support Menu links can define keyboard shortcuts using `$akey`. Examples: * `f7` * `v,alt+shift` BootstrapMenu: * Converts shortcut into JavaScript listeners * Adds visual hint to menu text * Triggers menu click programmatically This feature is **optional** and renderer-dependent. --- ## 14.7 menu.php: Project-Level Menu Definition The `menu.php` file is **project-specific**, not framework-specific. Responsibilities: * Define menus based on project needs * Apply role-based navigation * Include plugin menus conditionally Example behavior: * Guest users see Login * Logged-in users see User, Tools, Dashboard * Admin-only menus are protected via roles or permissions This file **does not render HTML** — it only **declares menus**. --- ## 14.8 Dynamic Menu Extension via Plugins Menu system supports modular extension: ```php include_once(PROJ_PATH . "/plugin/cmenu.php"); include_once(PROJ_PATH . "/plugin/cmebmenu.php"); include_once(PROJ_PATH . "/plugin/cadmmenu.php"); ``` Benefits: * Plugins can register menus independently * Core menu remains clean * Easy enable/disable features This makes SartajPHP **plugin-friendly by design**. --- ## 14.9 Master File Role in Rendering The **master file controls layout**, not logic. Key facts: * Master file is always PHP * It decides: * Where menu appears * Where Gate output appears * Which CSS/JS is global The master file does **not**: * Decide permissions * Generate menus * Control Gate logic --- ## 14.10 FrontPlace: Component Injection System FrontPlace is a **layout-level component system**. In the master file: * Components are registered using `addFrontPlace` * Executed using `runFrontPlace` * Rendered using `renderFrontPlace` This allows: * Menu as reusable component * Sidebar, header, footer injection * Clean layout composition --- ## 14.11 Rendering Pipeline in Master File The correct execution order is: 1. Register FrontPlaces 2. Run FrontPlaces (logic execution) 3. Output HTML layout 4. Render FrontPlaces at desired positions 5. Render Gate output 6. Inject framework JS & CSS 7. Send final HTML to browser `SphpBase::getAppOutput()` always renders **Gate output only**, never menu or layout. --- ## 14.12 Framework Asset Injection Framework manages assets centrally: * `getHeaderHTML()` → JS & CSS headers * `getFooterHTML()` → Footer scripts * Bootstrap & jQuery can be loaded once at master level This avoids: * Duplicate asset loading * App-level dependency conflicts --- ## 14.13 Why This Design Matters This architecture provides: * Permission-aware navigation * Fully customizable UI * Zero logic inside layout * Plugin-ready menu system * Clean separation of concerns SartajPHP does **not lock developers** into a UI system — it only provides **data and control**. --- ## 14.14 Summary In this chapter, you learned: * How SartajPHP menu system really works * Difference between menu data and menu renderer * How permissions control navigation visibility * Role of `menu.php` * Role of `master.php` * How FrontPlace composes final output This system is **framework-light, developer-powerful**, and suitable for **small to enterprise-scale projects**. --- # **Chapter 15: Creating a Mobile App with SartajPHP Mobile + Kotlin + Android Studio** ## Creating a Mobile App with SartajPHP Mobile + Kotlin + Android Studio --- ## 15.1 Introduction SartajPHP Mobile App is not a wrapper, emulator, or hybrid workaround. It is a **true mobile application architecture** where: * **UI is written in SartajPHP** * **JavaScript logic is structured via SJS** * **Native Android logic is written in Kotlin** * **Build system is pure Gradle** * **Android Studio and VS Code share the same project** This chapter explains how to create a **production-ready Android Mobile App** using: * SartajPHP MobileGate in PHP Extended With BasicGate * Kotlin Native bridge * SartajPHP KotlinGate in Kotlin * VS Code as the primary IDE * Android Studio as an optional native editor No code generation tools. No CLI wrappers. No Electron. No Cordova. --- ## 15.2 Unified Project Philosophy Unlike traditional frameworks, SartajPHP Mobile App uses **one shared project directory**. ``` project-root/ ├─ apps/ (SartajPHP Gate code) │ ├─ android (Android Files) │ │ ├─ config.xml (AndroidManifest.xml) │ │ ├─ MainActivity.kt (Main Activity) │ │ ├─ build.gradle.kts (Gradle Build File) │ │ ├─ java/ (Extra Java Files) │ │ ├─ res/ (Icon Files) │ │ ├─ cpp/ (C++ Files with CMakeLists.txt) │ │ ├─ libs (Extra Libraries Files) │ ├─ Index.gate.php (MobileGate which Convert Web Server Code as Browser Code) │ ├─ fronts/ (HTML base design in Front Files) │ ├─ jslib/ (Extra JS Files to run inside WebView on Mobile App) │ ├─ kotlin_apps/ (Design Gate Classes to handle requests Extend with KotlinGate From SartajPHP Kotlin SDK) │ └─ sjs/ (Sartaj JS Files, Easy Embed JS Event code to Front File on Server Side) ├─ www/ (Generated web assets) ├─ build/ (Generated Android Gradle Project OR it generate app folder for Android Studio) │ ├─ app/ │ ├─ settings.gradle.kts │ ├─ build.gradle.kts │ └─ gradlew ``` ### Key Idea * **Android Studio creates the project as Empty Activity** * **SartajPHP fills it** * **Gradle builds it** * **VS Code edits everything** This means: * Kotlin developers and PHP developers work together * No duplication of UI logic * Native APIs are first-class citizens **SartajPHP Mobile Gate Kotlin Model** JS call → KotlinGate created → process → destroyed JS call → KotlinGate created ↓ Access KotlinApi ↓ Get persistent Object ↓ Execute action ↓ Return response ↓ KotlinGate destroyed ✔ Object Store in KotlinApi stays alive ✔ UI remains stateless ✔ Very scalable --- ## 15.3 Creating the Android Project ### Step 1: Create Android Project (Once) Using **Android Studio**: * Create an **Empty Activity** * Language: **Kotlin** * Minimum SDK: As required * Project location: `build/` This step creates: * Gradle wrapper * settings.gradle.kts * AndroidManifest.xml * app module After this step, **Android Studio is optional**. --- ## 15.4 Creating SartajPHP Mobile App Open **VS Code**, then: ``` Ctrl + Shift + P → Create SartajPHP Mobile App ``` This creates: ``` apps/ ├─ Index.gate.php ├─ fronts/ (Front Files) │ ├─ index_main.front (Home Page of Mobile App) │ ├─ page2.front (Other Page) │ ├─ page3.front (Other Page) │ └─ toolbar.front (OnSen UI ToolBar to Insert into Pages) └─ sjs/ (Sartaj JS Files, Name should match with Front files) ├─ index_main.sjs (Home Page SJS File) ├─ page2.sjs (Other Page SJS File) └─ page3.sjs ``` The Android project remains untouched — SartajPHP **integrates**, it does not replace. --- ## 15.5 The MobileGate Entry Point The file `apps/Index.gate.php` extends `MobileApp`. ```php mobappname = "HelloSartajPHP"; // use autoversion //$this->mobappversion = "1.0.0"; $this->mobappdes = "A sample SartajPHP application that responds to the deviceready event."; $this->mobappauthor = "SartajPHP Team"; $this->mobappauthoremail = "sartajphp@gmail.com"; $this->mobappauthorweb = "https://sartajphp.com"; // force overwrite existing android project files //$this->setForceCopyGen(); // disable android studio project generation //$this->disableAndroidStudio(); $this->minSdkVersion = "25"; $this->targetSdkVersion = "34"; $this->compileSdkVersion = "34"; /* // add permissions $this->addAndroidSetting("permission", ''); // add reciver code $this->addAndroidSetting("receiver", ' '); // add service code $this->addAndroidSetting("service", ''); // add content provider code $this->addAndroidSetting("provider", ''); // anything extra inside application tag $this->addAndroidSetting("application", ''); // add in android section of build gradle $this->addAndroidSetting("android", ''); // add in dependencies section of build gradle $this->addAndroidSetting("dependency", ''); */ SphpJsM::addBootStrap(); $this->addDistLib($this->phppath . "/jslib/twitter/bootstrap5"); $this->addDistLib($this->phppath . "/jslib/onsen"); $this->addPage(new Sphp\tools\FrontFile($this->mypath . "/fronts/page2.front")); //$this->addPage(new Sphp\tools\FrontFile($this->mypath . "/fronts/page3.front")); // servers which access via ajax $hostlist = "http://domain.com "; $meta = ' '; $this->setSpecialMetaTag($meta); } } ``` ### What this does * Defines app identity * Injects UI libraries * Registers pages * Publishes Android assets automatically No manual Gradle edits required. --- ## 15.6 Front Files – UI Layer Front files define **UI components** using HTML-like syntax with server awareness. ### index_main.front ```html Home Settings toolbar
    System Status
    Ready to scan cameras
    ``` Front files: * Are rendered server-side * Become static assets for Android * Bind automatically to SJS --- ## 15.7 Toolbar and Page Reuse Components can be reused via ``. ```html
    App Title
    ``` This avoids UI duplication and keeps layouts consistent. --- ## 15.8 SJS – The Heart of SartajPHP Mobile SJS (Sartaj JavaScript) is **not normal JavaScript files**. It is: * Parsed server-side * Injected context-aware * Bound automatically to UI components ### Key Rule > **Only JavaScript code is allowed inside SJS functions** --- ## 15.9 SJS Function Prefix System SJS behavior is defined entirely by **function name prefixes**. ### `ofjs_` – Browser-Only Event Binding ```js function ofjs_p3_click(eventer) { alert("p3 click"); } ``` * Binds click event to element with `id="p3"` * Runs only on client * No server call --- ### `ofjs_*_comp_*_init` – Component-Scoped Binding ```js function ofjs_btnpage3_click_comp_page3_init(eventer) { onsen.loadPage("home.html"); } ``` * Binds only when component `page3` is active * Automatically unbound on destroy * Prevents memory leaks --- ### `jq_` – jQuery + Mobile Events ```js function jq_deviceready(eventer) { logMe("device ready"); } ``` Examples: * `jq_keyevent_global` * `jq_drag_global` * `jq_deviceready` These work without writing any jQuery boilerplate. --- ### `comp_` – Component Lifecycle Hooks ```js function comp_home_init(eventer) { logMe("home init"); } function comp_home_destroy(eventer) { logMe("home destroyed"); } ``` Mapped directly to Onsen UI lifecycle: * init * show * hide * destroy --- ### `onjs_` – Client + Server Event `onjs_` works like `ofjs_`, but also triggers: * BasicGate server event * AJAX call * Optional WebSocket communication Ideal for: * SIP calls * GSM gateway control * Native device operations --- ## 15.10 Calling Kotlin from JavaScript ```js Android.ShowToast("Hello from SartajPHP"); callKotlinGate("index", {}); callKotlinGateEvent("index","test","evtp", {}); ``` This bridges: * JavaScript → Kotlin → Native Android * No WebView hacks * No reflection abuse ```kotlin package com.sartajphp.gate class Sip : KotlinGate() { private fun getEngine(): SipEngine { if(!KotlinApi.isProp("sip.engine")){ val engine = SipEngine() engine.start() KotlinApi.addProp("sip.engine", engine) } return KotlinApi.getProp("sip.engine") as SipEngine } private fun ensureSipPermissions(): Boolean { val perms = arrayOf( "android.permission.RECORD_AUDIO" ) if(!KotlinApi.hasAllPermissions(*perms)){ KotlinApi.requestAllPermissions(*perms) return false } return true } override fun page_new() { if(!ensureSipPermissions()){ JSServer.addJSONHTMLBlock("sipstatus","Permission Required") JSServer.flush() return } val engine = getEngine() JSServer.addJSONHTMLBlock("sipstatus","SIP Engine Ready") JSServer.flush() } fun page_event_register(evtp:String){ val engine = getEngine() val user = data["user"].toString() val pass = data["pass"].toString() val domain = data["domain"].toString() engine.register(user, pass, domain) JSServer.addJSONHTMLBlock("sipstatus","Registered") JSServer.flush() } } ``` --- ## 15.11 Why This Architecture Matters | Feature | SartajPHP Mobile | | ----------- | ------------------------ | | UI | Server-generated | | JS | Context-aware | | Native | First-class | | IDE | VS Code + Android Studio | | Build | Pure Gradle | | Performance | Native WebView | This is **not hybrid** — this is **layered native architecture**. --- ## 15.12 Foundation for Mobile App With this structure, the Mobile Gate can: * Use **SJS File for bind UI events to FrontFile HTML Tags** * Use **KotlinGate for Native Programming** * Use **KotlinApi for Create and use Object for Long Processing and set and check Gate wide Permissions** * Use **MobileGate for Generating HTML,css,js and project Files and Configuration** * Use **FrontFile for Generating HTML,css,js and Components to make easy UI** * Use **WebSocket for real-time calls to NativeGate** * Use **AJAX for calls to BasicGate** * Use **Java Code for any native operations** * Use **C++ Code for any native operations** * Use **libs folder to use pre compiled libraries** No battery-heavy SphpServer required on device that is used for Gate develop for different purposes. --- ## 15.13 How to use C++ Code with SartajPHP MobileApp C++ code need external compiler tools like cmake. Put all your code inside your project folder apps/android/cpp folder with cmake and then you need add build settings in apps/android/build.gradle.kts file:- ``` android { ... externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" // relative path version "3.22.1+" // optional - your CMake version } } } ``` Create a cmkae CMakeLists.txt inside apps/android/cpp file:- ``` cmake_minimum_required(VERSION 3.22.1) project("MyNativeLib") add_library( # Creates your shared library native-lib # Name of the .so file → libnative-lib.so SHARED # Must be SHARED for Android native-lib.cpp # ← list ALL your .cpp files here myclass.cpp ) find_library(log-lib log) # Android logging library target_link_libraries( # Link libraries your code needs native-lib ${log-lib} ) ``` and example c++ file:- ```cpp #include #include extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapp_MainActivity_stringFromJNI(JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++!"; return env->NewStringUTF(hello.c_str()); } ``` and use in kotlin/java ```java class MainActivity : MainActivityBase() { external fun stringFromJNI(): String // declare native method companion object { init { System.loadLibrary("native-lib") // matches add_library name } } } ``` ## 15.13 Summary In this chapter, you learned: * How SartajPHP Mobile Gate integrates with Android Studio * How Front files define UI * How SJS binds JavaScript cleanly * How Kotlin and PHP coexist * Why this architecture scales for SIP Gateway Apps ---