Membersactivities framework client application development guide

16 Sep 2026 - dirkvm


MembersActivities Framework 1.0.31 — Client Application Developer Guide

MembersActivities Framework 1.0.31

Client Application Developer Guide
Version1.0.31
Controller Framework1.0.31
PHP8.3 or compatible PHP 8.x
DatabaseMySQL / MariaDB
AuthorDirk Van Meirvenne

Contents

  1. Introduction
  2. Technology Requirements
  3. Installing the Framework
  4. Recommended Client Application Structure
  5. Application Configuration
  6. Database
  7. MVC Application Flow
  8. Commands
  9. CommandDecorator
  10. Subscription Validation Strategy
  11. Request Object
  12. CSRF Protection
  13. Sessions
  14. Authentication and Login Levels
  15. AccessToken
  16. Payment Security
  17. Mollie Integration
  18. Mollie Webhook
  19. Models
  20. Mapper Usage and SQL Security
  21. Views and controls.xml
  22. Error Handling
  23. Header and Redirect Security
  24. Mail Queue and Cron
  25. Google Wallet
  26. Application-Specific Types
  27. Designing a New Client Screen
  28. Testing
  29. Deployment Checklist
  30. Architectural Summary

1. Introduction

The MembersActivities Framework is an application framework for building web applications that manage members, activities, subscriptions, payments and related functionality.

It is built on top of the Controller Framework and provides reusable domain functionality while allowing each client application to define its own user interface, business rules, subscription validation, activity types, payment descriptions, authentication configuration, mail configuration and optional integrations.

Client Application │ ├── Client Commands ├── Client CommandDecorators ├── Client Views ├── Client Strategies └── Client configuration │ ▼ MembersActivities Framework 1.0.31 │ ▼ Controller Framework 1.0.31

The client application should normally extend and configure the framework rather than modify framework source code.

2. Technology Requirements

ComponentRequirement
PHP8.3 or compatible PHP 8.x
Controller Framework1.0.31
MembersActivities Framework1.0.31
DatabaseMySQL / MariaDB
Database accessPDO
Web serverApache or compatible PHP web server
ComposerRequired

The framework uses a MySQL/MariaDB PDO connection. Oracle is not a supported database platform for this release.

3. Installing the Framework

A client application installs the framework through Composer:

{
    "require": {
        "samoscon/membersactivities-framework": "^1.0.31"
    }
}

The MembersActivities Framework requires samoscon/controller-framework ^1.0.31. Composer installs that dependency automatically.

composer install

Use composer install for deployments based on a committed composer.lock.

4. Recommended Client Application Structure

application/
│
├── index.php
├── composer.json
├── composer.lock
│
├── config/
│   └── app_options.ini
│
├── MVCFramework/
│   ├── controls.xml
│   ├── commands/
│   │   ├── DefaultCommand.php
│   │   ├── admin/
│   │   ├── user/
│   │   └── mollie/
│   ├── model/
│   └── views/
│
└── assets/

The exact directory structure may be adapted, but framework classes should remain in the Composer package and client-specific classes should remain in the client application.

5. Application Configuration

Configuration contains application-specific values such as environment, paths, database credentials, mail settings and security secrets.

[config]
environment=development
templatepath=/MVCFramework/views
controlsfile=/MVCFramework/controls.xml
loggingpath=/assets/logging/

[globals]
APP=MyApplication
_APPDIR=https://www.example.com/
_HOMEPAGE=https://www.example.com/
_ASSETDIR=https://www.example.com/assets/

_SALTRAND=...
_RAND=...

_DBUSER=...
_DBPASSWORD="..."
_DBNAME=...
_DBHOST=localhost
Security: values such as database passwords, Mollie credentials, Google credentials and especially _SALTRAND must remain secret. The configuration file must not be publicly downloadable.

_SALTRAND is used by the Controller Framework's AccessToken implementation. It should be a long, cryptographically random secret.

6. Database

The reference schema is intended for a new client application. Principal tables include:

member
activity
costitem
subscription
payment
remember_tokens
mail_queue
Member ├── Subscriptions ├── Payments └── Remember Tokens Activity ├── Cost Items └── child Activities Subscription ├── Member ├── Cost Item └── optional Payment Payment └── Member
Existing production database: do not replace it with the reference schema. Back it up and apply controlled schema changes or migrations.

7. MVC Application Flow

Browser │ ▼ index.php │ ▼ Controller Framework │ ▼ Command Resolver │ ▼ Client Command / CommandDecorator │ ▼ MembersActivities Command │ ▼ Model │ ▼ Database

Commands return statuses such as CMD_DEFAULT, CMD_OK, CMD_CONTINUE and CMD_ERROR. Routing and rendering are defined through the application's controller configuration.

8. Commands

A client command normally extends:

\controllerframework\controllers\Command

Example:

namespace commands\user;

class MyCommand extends \controllerframework\controllers\Command
{
    public function doExecute(
        \controllerframework\registry\Request $request
    ): int {
        // Application logic
        return self::CMD_DEFAULT;
    }

    protected function getLevelOfLoginRequired(): void
    {
        $this->setLoginLevel(
            new \controllerframework\sessions\NoLoginRequired()
        );
    }
}

A command should obtain and validate request data, perform the required operation, prepare response data and return an appropriate command status.

9. CommandDecorator

The CommandDecorator is one of the most important extension mechanisms. It allows client-specific behaviour to be added around a reusable MembersActivities command.

class PublicActivityCommand
    extends \controllerframework\controllers\CommandDecorator
{
    public function doExecuteDecorator(
        \controllerframework\registry\Request $request
    ): ?int {
        $request->set(
            'validator',
            new \model\SubscriptionValidationPublic()
        );
        return null;
    }

    public function initCommand(): void
    {
        $this->setCommand(
            new \membersactivities\commands\user\ActivityCommand()
        );
    }

    protected function getLevelOfLoginRequired(): void
    {
        $this->setLoginLevel(
            new \controllerframework\sessions\NoLoginRequired()
        );
    }
}

This pattern avoids modifying generic framework commands for every possible client application's business rule.

10. Subscription Validation Strategy

Subscription validation uses SubscriptionValidationStrategy. A client application provides a concrete strategy containing its own business rules.

class SubscriptionValidationUser
    extends \membersactivities\model\subscriptions\
             SubscriptionValidationStrategy
{
    protected function doCheckSubscription(
        \controllerframework\members\Member $member,
        \membersactivities\model\activities\Costitem $costitem
    ): array {
        if (!$member->active) {
            return $this->errorcode(
                200,
                'Your membership is inactive.'
            );
        }

        return $this->errorcode(0);
    }
}

The framework can then call the strategy before creating a subscription.

Security rule: never instantiate a validator class directly from a request parameter. Use a controlled CommandDecorator to select the concrete strategy.

11. Request Object

The Controller Framework's Request object transports information through the command chain.

$request->get('id');

$request->set('validator', $validator);

$request->addFeedback('Something went wrong.');

$request->set(
    'forwardqueryparams',
    ['id' => $id]
);

The Request object is a transport mechanism, not a trust boundary. Data originating from a browser must still be validated.

12. CSRF Protection

State-changing requests should use POST and CSRF protection.

// Generate token
$responses['csrf_token'] = $this->getCsrfToken();

// Validate token
if (!$this->validateCsrfToken($request)) {
    $request->set('errorcode', 'InvalidCsrfToken');
    return self::CMD_ERROR;
}
GET │ ├── display form └── generate CSRF token │ ▼ POST │ ├── validate CSRF token ├── validate input ├── perform state change └── return status

Do not implement state-changing operations through GET requests.

13. Sessions

CSRF protection requires an active session. Production applications should use secure session cookies with appropriate Secure, HttpOnly and SameSite settings and should use HTTPS.

14. Authentication and Login Levels

Commands define the required authentication level. Typical levels are:

new \controllerframework\sessions\NoLoginRequired()
new \controllerframework\sessions\UserLogin()
new \controllerframework\sessions\AdminLogin()

Administrative commands that expose or modify sensitive data should normally require AdminLogin.

15. AccessToken

Controller Framework 1.0.31 provides \controllerframework\security\AccessToken for protected public operations.

$accessToken =
    \controllerframework\security\AccessToken::generate(
        'mollie-order',
        (string) $orderid
    );

\controllerframework\security\AccessToken::validate(
    'mollie-order',
    (string) $orderid,
    $accessToken
);

The purpose and identifier form part of the security boundary. Tokens should only be transmitted over HTTPS and should not be unnecessarily logged.

16. Payment Security

Payment amounts must come from trusted server-side data. A browser must never be allowed to determine the amount sent to Mollie.

Browser │ │ order ID ▼ Server │ ├── retrieve Payment ├── read trusted amount └── create Mollie payment

The payment object should be loaded from the database and its server-side amount used when creating the payment.

17. Mollie Integration

Mollie integration is optional. Relevant commands include:

PaymentToMollieCommand
OrderToMollieCommand
WebhookFromMollieCommand
PaymentConfirmationCommand

The generic payment command can be decorated by the client application.

17.1 Application-specific order descriptions

The payment amount is obtained from the server-side Payment object. The order description can be supplied by the client-specific CommandDecorator through the Request:

$request->set(
    'orderDescription',
    'Concert tickets - May 2027'
);

The underlying OrderToMollieCommand reads this value. This allows different order types to have different descriptions without modifying the generic framework command.

18. Mollie Webhook and Payment Confirmation

The webhook is a machine-to-machine endpoint rather than a normal user interface. It should retrieve the current payment status from Mollie and update the corresponding server-side payment.

The customer redirect is not the authoritative source of payment status.

Payment confirmation should validate the protected order identifier and its AccessToken before displaying information associated with a payment.

19. Models

Core domain models include:

Activity
ActivityComposite
Costitem
Member
Subscription
Payment
GoogleWalletTicket

The client application should use the model API where possible instead of directly manipulating database records.

20. Mapper Usage and SQL Security

The framework uses mapper classes such as ActivityMapper, CostitemMapper, PaymentMapper and SubscriptionMapper.

For example:

$payment = \model\Payment::find($id);

Special care is required with Mapper::findAll(), because it accepts a free SQL select clause.

$mapper->findAll($selectClause);
SQL security: never concatenate untrusted user input into a free SQL clause. Validate and whitelist values before they are incorporated into SQL, and prefer the framework's model/database mechanisms where possible.

21. Views and controls.xml

Views are responsible for presentation. Commands should prepare data and pass it to the view.

Command routing is defined in controls.xml. A simplified example:

<command
    path="/activity"
    class="\commands\user\PublicActivityCommand">

    <view name="/user/activity" />

    <status value="CMD_ERROR">
        <view name="/errorView" />
    </status>

</command>

22. Error Handling

Use the Controller Framework's centralized error handling for unexpected exceptions.

try {
    $activity = \model\Activity::find($id);
}
catch (\Throwable $ex) {
    \controllerframework\error\ErrorHandler
        ::handleException($ex);

    $request->addFeedback(
        'Unable to retrieve the requested activity.'
    );

    return self::CMD_ERROR;
}

Do not expose stack traces or exception dumps to users. Most importantly, do not catch an exception and then continue as if the failed operation had succeeded.

23. Header and Redirect Security

Take special care with values used in:

Validate or whitelist such values before placing them in HTTP headers. When constructing query strings, prefer http_build_query() to manual concatenation.

24. Mail Queue and Cron

The framework supports asynchronous mail processing through the mail_queue table. Typical statuses include:

pending
sending
sent
failed

A typical workflow is:

Create mail queue records │ ▼ Return response to user │ ▼ Cron job │ ▼ Process mail queue

Cron jobs should use absolute paths:

php /absolute/path/to/application/index.php cleanupMailQueue

Do not rely on the current working directory in cron jobs.

25. Google Wallet

Google Wallet integration is optional. Applications using it require appropriate Google credentials, an issuer ID and application configuration. Credential files should be kept outside publicly accessible locations whenever possible.

26. Application-Specific Types

The framework provides extension points such as:

ActivityTypeImplementation
CostitemTypeImplementation
PaymentTypeImplementation
SubscriptionTypeImplementation

Client-specific business rules should be implemented in these extension points where appropriate rather than accumulating all business logic in controllers.

27. Designing a New Client Screen

Step 1 — Define the route

Add the route to controls.xml.

Step 2 — Create a client CommandDecorator

namespace commands\user;

class MyActivityCommand
    extends \controllerframework\controllers\CommandDecorator
{
    public function doExecuteDecorator(
        \controllerframework\registry\Request $request
    ): ?int {
        // Application-specific preparation
        return null;
    }

    public function initCommand(): void
    {
        $this->setCommand(
            new \membersactivities\commands\user\ActivityCommand()
        );
    }

    protected function getLevelOfLoginRequired(): void
    {
        $this->setLoginLevel(
            new \controllerframework\sessions\UserLogin()
        );
    }
}

Step 3 — Add application-specific strategies

$request->set(
    'validator',
    new \model\SubscriptionValidationUser()
);

Step 4 — Add the view

Create the appropriate template.

Step 5 — Configure status handling

<status value="CMD_ERROR">
    <view name="/errorView" />
</status>

Step 6 — Test

Test authenticated and unauthenticated users, invalid identifiers, invalid POST data, invalid CSRF tokens and expected failure paths.

28. Testing a New Release

Public functionality

Member functionality

Administrator functionality

Integration functionality

Security

29. Deployment Checklist

30. Architectural Summary

MembersActivities Framework 1.0.31 is intended to be used as a reusable domain framework rather than as a complete application.

┌─────────────────────────────────────────┐ │ Client Application │ │ │ │ Commands / Decorators / Views │ │ Strategies / Business Rules │ │ Configuration / Integrations │ └────────────────────┬────────────────────┘ │ ┌────────────────────▼────────────────────┐ │ MembersActivities Framework │ │ │ │ Members / Activities / Subscriptions │ │ Payments / Mail / Mollie / Wallet │ └────────────────────┬────────────────────┘ │ ┌────────────────────▼────────────────────┐ │ Controller Framework 1.0.31 │ │ │ │ MVC / Commands / Request / Sessions │ │ Security / CSRF / AccessToken / PDO │ │ Error handling / Audit / Rendering │ └─────────────────────────────────────────┘

The key development principle is:

Keep generic functionality in the framework and implement client-specific behaviour through decorators, strategies, models, commands, configuration and views.

This makes client applications easier to maintain and allows framework upgrades without unnecessarily modifying application code.