Step-by-Step Guide

This Step-by-Step Guide is designed to help you get started with the DotApp PHP Framework. It walks you through the core concepts, setup, and creation of your first modular application. DotApp is built with a modular philosophy, prioritizing flexibility and scalability. Follow these steps to understand its workflow and build a simple application from scratch.

Philosophy

The DotApp PHP Framework is built with modularity at its core. From the ground up, DotApp is designed to provide a robust and scalable foundation for modern web applications, prioritizing modular architecture to ensure flexibility, maintainability, and efficiency.

Why Modular Design?

Modularity is the heart of DotApp’s philosophy. By structuring applications as a collection of independent, reusable modules, DotApp enables developers to:

  • Build scalable applications with clear separation of concerns.
  • Reuse components across projects, reducing development time.
  • Maintain and update specific parts of an application without affecting the whole system.
  • Integrate new features or third-party tools seamlessly.

This approach ensures that your projects remain organized and adaptable, whether you're building a small prototype or a large-scale enterprise application.

Robust Foundation, Recommended Practices

DotApp provides a solid foundation with tools and conventions tailored for modular development. In this guide, we focus on the recommended practices that align with DotApp’s design goals:

  • Module-Centric Workflow: Organize your application into self-contained modules for clarity and scalability.
  • Consistent Structure: Follow DotApp’s conventions for controllers, templates, and configurations to streamline collaboration.
  • Best Practices: Leverage built-in tools for routing, templating, and module management to avoid common pitfalls.
  • Future-Proofing: Build with modularity to make future expansions or refactoring effortless.

While DotApp is flexible enough to support alternative approaches, this guide emphasizes the methods that best utilize its modular architecture. We aim to teach techniques that maximize the framework’s strengths and help you avoid inefficient or error-prone patterns.

What’s Next?

Ready to start building with DotApp? Head to the Installation section to set up the framework and begin your modular journey. For a deeper dive into creating your first module, check out the First Module & Setup section.

Proudly made in Slovakia 🇸🇰

Installation

Installing the DotApp PHP Framework is quick and flexible. Choose one of the three methods below to set up your project. Each method results in the same modular project structure, ready for development.

Option 1: Git Clone

If you have Git installed, you can clone the DotApp repository directly. Run the following command in your terminal:


git clone https://github.com/dotsystems-sk/dotapp.git ./
    

This creates a DotApp project in your current directory. Don’t have Git? No problem—try one of the other methods.

Option 2: DotApper CLI

Download the dotapper.php CLI tool and use it to install DotApp. Follow these steps:

  1. Download the file: dotapper.php.
  2. Save it to your project directory.
  3. Run the installation command:
    
    php dotapper.php --install
                

This sets up DotApp with all necessary dependencies.

Option 3: ZIP Download

Prefer a manual approach? Download the DotApp ZIP file and extract it:

  1. Download the ZIP: DotApp main.zip.
  2. Extract the contents to your project directory.

Once extracted, your project is ready to use.

Project Structure

After installation, your project directory will have the following modular structure:


├───app
│   ├───custom.classes
│   │   └───phpmailer
│   ├───modules
│   ├───parts
│   │   ├───Controllers
│   │   ├───Middleware
│   │   ├───Models
│   │   └───views
│   ├───runtime
│   │   ├───cache
│   │   ├───generator
│   │   ├───logs
│   │   └───routercache
│   └───vendor
│       ├───bin
│       └───composer
└───assets
    ├───dotapp
    └───modules
    

This structure is designed for modularity, with the modules folder as the core of your application. You’ll notice the parts directory contains folders like Controllers, Middleware, Models, and views. These exist to support alternative programming approaches that DotApp allows. However, this guide focuses on the recommended modular workflow, using the modules directory to keep your application scalable and maintainable. We won’t cover non-modular techniques, as our goal is to teach the best practices for DotApp development.

What’s Next?

With DotApp installed, you’re ready to create your first module. Head to the First Module & Setup section to start building your modular application.

First Module & Setup

With the DotApp PHP Framework installed, you’re ready to create your first module. Modules are the core of DotApp’s modular architecture, allowing you to organize your application into reusable, self-contained components. In this section, we’ll create a HelloWorld module and configure it to handle the homepage route.

Creating the Module

Use the DotApper CLI to generate a new module. Run the following command in your project directory:


php dotapper.php --create-module=HelloWorld
    

You’ll see the output:


Module successfully created in: ./app/modules/HelloWorld
    

This creates a new HelloWorld module in the app/modules directory.

Module Structure

The HelloWorld module has the following structure:


├───modules
│   │   .gitkeep
│   │
│   └───HelloWorld
│       │   module.init.php
│       │   module.listeners.php
│       │
│       ├───Api
│       │       Api.php
│       │
│       ├───assets
│       │       howtouse.txt
│       │
│       ├───Controllers
│       │       Controller.php
│       │
│       ├───Libraries
│       ├───Middleware
│       ├───Models
│       ├───translations
│       └───views
│           │   clean.view.php
│           │
│           └───layouts
│                   example.layout.php
    

Here’s what each file and directory is for:

  • module.init.php: Defines the module’s routes and initialization conditions, controlling when and how the module is loaded.
  • module.listeners.php: Registers event listeners for the module, allowing it to respond to framework events like module loading.
  • Api/Api.php: A sample API controller for building API endpoints (can be deleted or ignored).
  • assets/: Stores module-specific assets like CSS, JavaScript, or images. Contains a howtouse.txt guide for beginners.
  • Controllers/Controller.php: A sample controller (can be deleted or ignored).
  • Libraries/: Holds custom PHP libraries or classes specific to the module.
  • Middleware/: Contains middleware classes for request processing, such as authentication or validation.
  • Models/: Stores model classes for database interactions or business logic.
  • translations/: Manages language files for internationalization.
  • views/: Contains view templates, including clean.view.php (a sample view) and layouts/example.layout.php (a sample layout), both of which can be deleted or ignored.

The sample files (Api.php, Controller.php, clean.view.php, example.layout.php) are included as examples for beginners. In this guide, we’ll create our own controller and views, so you can safely delete or ignore these files.

Configuring the Module

Let’s configure the HelloWorld module to handle the homepage route if no other module claims it.

Step 1: Set Up Event Listeners

Open app/modules/HelloWorld/module.listeners.php and modify the register function to redirect the homepage (/) to /helloworld/ if no other module defines a default route:


public function register($dotApp) {
    $dotApp->on("dotapp.modules.loaded", function($moduleObj) use ($dotApp) {
        if (!$dotApp->router->hasRoute("get", "/")) {
            // No default route is defined, so set this module's route as the default
            $dotApp->router->get("/", function() {
                header("Location: /helloworld/", true, 301);
                exit();
            });
        }
    });
}
    

This code listens for the dotapp.modules.loaded event, which fires after all modules are loaded. If no module has claimed the / route, we set a redirect to /helloworld/, making our module the default homepage.

DotApp supports several module-specific events:

  • dotapp.module.HelloWorld.init.start: Fired when module initialization begins.
  • dotapp.module.HelloWorld.init.loading: Fired when the module’s main functions (e.g., routes) start loading, if initialization conditions are met.
  • dotapp.module.HelloWorld.init.loaded: Fired after the module’s routes and functions are loaded.
  • dotapp.module.HelloWorld.init.end: Fired when module initialization ends, regardless of whether conditions were met.

For this example, dotapp.modules.loaded is all we need.

Step 2: Configure Module Initialization

Open app/modules/HelloWorld/module.init.php to define when the module should activate. Modify the initializeRoutes function to specify that the module activates for routes starting with /helloworld:


public function initializeRoutes() {
    $routes = [];
    $routes[] = '/helloworld*';
    return $routes;
}
    

This ensures the module only activates for URLs starting with /helloworld (e.g., /helloworld, /helloworld/, /helloworld/sekcia). Using ['*'] (activating for all URLs) is less efficient and not recommended for large projects, so we optimize by specifying our route prefix.

Next, configure the initializeCondition function to determine if the module should initialize based on the route match. By default, set it to:


public function initializeCondition($routeMatch) {
    return $routeMatch;
}
    

This activates the module whenever a route from initializeRoutes matches (e.g., /helloworld*). You can add custom logic to control initialization. For example, to only activate the module in 2025:


public function initializeCondition($routeMatch) {
    if ($routeMatch === true) {
        if (date("Y") == 2025) return true;
    }
    return false;
}
    

This checks if the route matches and the current year is 2025. For this guide, we’ll keep the default behavior (return $routeMatch;) to activate the module whenever the route matches.

Step 3: Define Initial Routes

In the same module.init.php file, update the initialize function to define static routes for the module:


public function initialize($dotApp) {
    // Static routes
    Router::get(['/helloworld','/helloworld/'], "HelloWorld:Pages@index", Router::STATIC_ROUTE);
}
    

This sets up static routes for /helloworld and /helloworld/, pointing to the index method of the Pages controller in the HelloWorld module. Specifying Router::STATIC_ROUTE optimizes performance by avoiding dynamic route matching (unlike the default Router::DYNAMIC_ROUTE).

What’s Next?

Your HelloWorld module is now created and configured. Next, we’ll create the Pages controller to handle the /helloworld route. Head to the First Controller section to continue.

First Controller

With your HelloWorld module configured, it’s time to create a controller to handle the /helloworld route. In the DotApp PHP Framework, controllers manage the logic for specific routes within a module. In this section, we’ll create the Pages controller to prepare for our first "Hello World" output.

Creating the Controller

Use the DotApper CLI to generate the Pages controller for the HelloWorld module. Run the following command in your project directory:


php dotapper.php --module=HelloWorld --create-controller=Pages
    

You’ll see the output:


Controller 'Pages' successfully created!
    

This creates a new controller file at app/modules/HelloWorld/Controllers/Pages.php.

Setting Up the Controller

Open app/modules/HelloWorld/Controllers/Pages.php and add the index method to handle the /helloworld route we defined in the module.init.php file. Replace the file’s controller class contents with the following code:


    public static function index($request)
    {
        return "<h1>Hello World !</h1>";
    }
    

This code does the following:

  • Defines the Pages controller in the DotApp\Modules\HelloWorld\Controllers namespace.
  • Adds a static index method that takes a $request parameter and returns a simple HTML string: <h1>Hello World !</h1>.

The index method is linked to the /helloworld and /helloworld/ routes via the Router::get call in module.init.php from the previous section. When these routes are accessed, this method will execute and output the "Hello World" message.

What’s Next?

Your Pages controller is now set up to handle the /helloworld route. In the next section, we’ll test the application to see the "Hello World" output in action. Head to the Hello World section to complete the setup and view your first DotApp page.

Hello World

Your HelloWorld module and Pages controller are now set up in the DotApp PHP Framework. In this section, we’ll test the application to see your first "Hello World" page in action, confirming that everything works as expected.

Testing Your Application

To see your "Hello World" output, you need a web server running with PHP. If you haven’t set one up yet, you can use PHP’s built-in development server for testing. Run the following command in your project directory:


php -S 127.0.0.1:8000
    

This starts a server on http://127.0.0.1:8000. Alternatively, use your preferred server setup (e.g., Apache, Nginx) with the project directory as the document root.

Viewing the Hello World Page

Open your browser and navigate to http://127.0.0.1:8000/ (or your server’s address). You’ll be redirected to http://127.0.0.1:8000/helloworld/, where you’ll see:


<h1>Hello World !</h1>
    

This output comes from the index method in app/modules/HelloWorld/Controllers/Pages.php, which we set up in the First Controller section. The redirect from / to /helloworld/ is handled by the event listener in module.listeners.php, configured in the First Module & Setup section.

Understanding the Flow

Here’s how it all comes together:

  • The HelloWorld module is activated for routes starting with /helloworld, as defined in module.init.php.
  • If no other module claims the homepage (/), the event listener in module.listeners.php redirects to /helloworld/.
  • The /helloworld/ route is mapped to the Pages controller’s index method via the static route in module.init.php.
  • The index method returns the HTML string

    Hello World !


    which is displayed in the browser.

This modular workflow showcases DotApp’s simplicity and power, allowing you to build scalable applications with clear, reusable components.

Congratulations

You’ve built and tested your first DotApp application. Next, explore the template system to create dynamic views. Head to the Introduction to Template System section to learn how to enhance your application with templates.

Introduction to Template System

The DotApp PHP Framework includes a powerful templating system that simplifies creating dynamic, reusable views for your modular applications. In this section, we’ll explore the basics of DotApp’s templating system, including views, layouts, and variables, to prepare you for building dynamic pages.

What is the Templating System?

DotApp’s templating system allows you to separate presentation logic from business logic, making your application easier to maintain and scale. It consists of:

  • Views: PHP files (e.g., helloworld.view.php) that contain HTML, dynamic content, and multiple layouts, rendered for specific routes.
  • Layouts: Reusable templates (e.g., h1-test.layout.php) included within views, providing modular components like headers, footers, or sections.
  • Variables: Data passed from controllers to views/layouts using the Renderer class, accessible via {{ var: $variableName }}.
  • Form Handling: Built-in tools like {{ formName(name) }} and {{ CSRF }} for secure form processing.

Views and layouts are stored in the views directory of a module (e.g., app/modules/HelloWorld/views), with layouts often organized in a layouts subfolder for clarity.

Key Features

The templating system is designed for flexibility and security:

  • Dynamic Rendering: Use the Renderer class to load views, set variables, and render output.
  • Multiple Layout Inclusion: A single view can include multiple layouts using {{ layout: layoutName }}, with support for subfolders (e.g., {{ layout: subfolder/layoutName }}).
  • Secure Forms: Use {{ formName(name) }} to name and secure forms, enhancing CSRF protection.
  • Modular Design: Templates are module-specific, keeping your application organized and reusable.

What’s Next?

In the next section, we’ll extend our HelloWorld module to use the templating system, adding a dynamic form and rendering it with a view and layout. Head to the Hello World in Template System section to see it in action.

Hello World in Template System

Let’s enhance our HelloWorld module to use the DotApp PHP Framework templating system. We’ll add a new route, create a view and layout, and update the controller to render a dynamic form. This example demonstrates how to combine views, layouts, and secure form handling.

Step 1: Add a New Route

Open app/modules/HelloWorld/module.init.php and add a dynamic route for /helloworld2. Append the following line to the initialize function:


Router::match(['get','post'],"/helloworld2(?:/)?","HelloWorld:Pages@index2");
    

This dynamic route handles both GET and POST requests for /helloworld2 or /helloworld2/, pointing to the index2 method of the Pages controller. Dynamic routes (the default) use regular expressions for flexibility, unlike the static routes used previously.

Step 2: Create the View

Create a new file named helloworld.view.php in app/modules/HelloWorld/views/. Add the following code:


{{ layout:h1-test }}
<br>
<br>
<h2>Form test:</h2>
<br>
<form class="search-form" method="POST">
    <input type="text" placeholder="Enter text to display" name="displaytext">
    {{ formName(testFormName) }}
    <!--
        Using only {{ CSRF }} is not recommended, but it is your choice.
        I suggest using formName, for example formName(testFormName), which provides much stronger protection against attacks than a regular CSRF.
        On the processing side, you then verify $request->form(['POST'],"testFormName"...
        This function is used to distinguish forms by name, but it adds strong security,
        so even if you don't need to differentiate forms by name, use it to secure the form.
    -->
    <button type="submit" class="btn search-btn">{{ var: $btnText }}</button>
</form>
<!--
	$_GET, $_POST, etc. are already protected against XSS, code injection, and similar attacks.
	To remove protection: DotApp::DotApp()->unprotect($displayText); // Recursively unprotects the variable (supports arrays, references, etc.)
	Do NOT assign the result! The function modifies the variable by reference.
-->
{{ var: $displayText }}
{{ var: $securityData }}
    

This view includes:

  • {{ layout:h1-test }}: Loads the h1-test layout.
  • A form with a text input, a secure form name ({{ formName(testFormName) }}), and a submit button using a variable ({{ var: $btnText }}).
  • Variables ({{ var: $displayText }}, {{ var: $securityData }}) to display form output.

Step 3: Create the Layout

Create a new file named h1-test.layout.php in app/modules/HelloWorld/views/layouts/. Add the following code:


<h1>{{ var: $testVar }}</h1>
    

This layout displays a variable ({{ var: $testVar }}) as an h1 heading. Layouts can be organized in subfolders (e.g., {{ layout:subfolder/h1-test }} loads views/subfolder/h1-test.layout.php).

Step 4: Update the Controller

Open app/modules/HelloWorld/Controllers/Pages.php and add the index2 method below the existing index method:


public static function index2($request, Renderer $renderer) {
    /*
        Request::form() facade for $request->form() (use Dotsystems\App\Parts\Request;)
        $request->form(string "testFormName", success_callback, error_callback)
        $request->form(array "method", string "testFormName", success_callback, error_callback)
        success_callback - main logic
        error_callback - data manipulation detected
    */
    $displayText = "";
    $securityData = "";
    // Verify the form's origin by its name (since we only process the form for POST, specify the POST method)
    $request->form(['POST'], "testFormName", function($request) use (&$displayText, &$securityData) {
        $displayText = '<h3>'.$request->data()['displaytext']."</h3>";
        $securityData = '<hr>Your data was secured using: <pre>'.print_r($request->data(), true)."</pre>";
    });
    $testVar = "Hello World";
    $btnText = "Send";
    // $renderer is obtained via dependency injection Renderer $renderer
    $rendered1 = $renderer->module(self::moduleName()) // Set to use views and layouts from the current module
        ->setView("helloworld") // Select the view (/app/modules/HelloWorld/views/helloworld.view.php)
        ->setViewVar("testVar", $testVar) // Create variable $testVar available in the view and all nested layouts
        ->setViewVar("btnText", $btnText) // Create variable $btnText available in the view and all nested layouts
        ->setViewVar("displayText", $displayText) // Create variable $displayText available in the view and all nested layouts
        ->setViewVar("securityData", $securityData) // Create variable $securityData available in the view and all nested layouts
        ->renderView();
    return($rendered1);
}
    

This method:

  • Processes the form using $request->form, validating the form name (testFormName) and POST method.
  • Sets variables ($testVar, $btnText, $displayText, $securityData) for the view.
  • Uses the Renderer to load the helloworld view, assign variables, and render the output.

Testing the Template System

Ensure your server is running (e.g., php -S 127.0.0.1:8000). Open http://127.0.0.1:8000/helloworld2 in your browser. You’ll see a form with a "Hello World" heading (from the layout). Enter text, submit the form, and the page will display the submitted text and security data below the form, rendered securely.

Great !

You’ve now built a dynamic page using DotApp’s templating system, complete with a secure form and modular structure. To explore more features, check out the full DotApp Documentation or experiment with additional views, layouts, and modules in your HelloWorld project.

Try It Live

You’ve built the HelloWorld module and explored the DotApp PHP Framework templating system. Now, you can see the results in action without setting up a local server! The HelloWorld module from this guide is deployed and active on our demo server. Try it out to experience the framework’s modular design firsthand.

Test the Hello World Page

Visit the following URL to see the basic "Hello World" output from the Hello World section:


https://dotapp.dev/helloworld
    

Click here to visit https://dotapp.dev/helloworld

This page displays <h1>Hello World !</h1>, served by the Pages controller’s index method.

Test the Templated Form

To explore the dynamic form from the Hello World in Template System section, visit:


https://dotapp.dev/helloworld2
    

Click here to visit https://dotapp.dev/helloworld2

This page renders a form using the helloworld.view.php view and h1-test.layout.php layout. Enter text, submit the form, and see the secure output, powered by DotApp’s templating system and form handling.

Why Try It Live?

Testing the live demo helps you:

  • Verify the module’s functionality without local setup.
  • Understand how DotApp’s modular architecture works in a real environment.
  • Experiment with the form to see secure data processing in action.

What’s Next?

Now that you’ve seen the HelloWorld module live, dive deeper into the DotApp PHP Framework by exploring advanced features, creating new modules, or customizing templates. Visit the full DotApp Documentation for more guides and examples to continue your journey.