Perfex CRM Module Development Guide (2026)

Perfex CRM, built on the CodeIgniter PHP framework, supports a modular architecture that allows developers to build custom modules to extend its core functionality. As the official Perfex CRM documentation notes, modules can include controllers, models, views, language files, libraries, hooks, and more.

In this guide, we’ll walk through how to structure, build, and deploy a custom module in Perfex CRM, covering all the major parts: initialization, controllers, models, views, hooks, language files, options, security, and even payment gateway integration.

1. Perfex CRM Module Prerequisites & Setup

Before you start:

  1. Perfex CRM Version: Make sure you’re using a version that supports modules (module documentation is valid from v2.3.2+).
  2. Enable Development Mode: To catch errors or warnings, turn on development mode in your Perfex CRM instance.
  3. Familiarity with CodeIgniter: Since Perfex is based on CodeIgniter, knowing its MVC structure, how controllers and models work, and how to access the $CI instance is very important.

2. Module Directory Structure

Every module lives under the modules/ directory in your Perfex CRM installation. Perfex CRM+1 Here is a typical structure:

modules/
  my_module/
    my_module.php           // init file
    controllers/
      Admin_my_module.php   // admin controller
      Clients_my_module.php // client area controller (optional)
    models/
      My_module_model.php
    views/
      admin/
        index.php
      public/
        index.php
    libraries/
      My_module_lib.php
    language/
      english/
        my_module_lang.php
    assets/
      css/
      js/
  • The init file (my_module.php) is mandatory. Its name must match the module folder name.
  • Controllers: separate admin (AdminController) and client (ClientsController) controllers.
  • Models: for database interaction.
  • Views: separate views for admin and client.
  • Libraries: for shared logic or helper classes.
  • Language files: store translatable text.
  • Assets: CSS / JS if your module needs UI.

3. Perfex CRM Module Init File (Metadata + Hooks)

The init file is the starting point. It provides the module’s metadata, and where you register activation, deactivation, and uninstall hooks.

Example modules/my_module/my_module.php :

<?php
defined('BASEPATH') or exit('No direct script access allowed');

/*
Module Name: My Module
Description: This is a sample custom module for Perfex CRM.
Version: 1.0.0
Requires at least: 2.3.*
Author: Your Name
*/

register_activation_hook('my_module', 'my_module_activate');
register_deactivation_hook('my_module', 'my_module_deactivate');
register_uninstall_hook('my_module', 'my_module_uninstall');

function my_module_activate() {
    // Code to run when module is activated
    // e.g., create database tables
}

function my_module_deactivate() {
    // Code to run when module is deactivated
}

function my_module_uninstall() {
    // Code to run when module is uninstalled (cleanup)
}

// Hooks / filters example
hooks()->add_action('admin_init', 'my_module_admin_init');

function my_module_admin_init() {
    // add menu items or other admin logic
}

Key points:

  • Use unique function prefixes (e.g., my_module_) to avoid collisions.
  • You can register Cron tasks, payment gateways, or language files using helper functions provided by Perfex.

4. Controllers

Controllers handle HTTP logic. For modules, you typically create controllers under controllers/.

  • AdminController: for staff / admin side.
  • ClientsController: for client portal side.

Example modules/my_module/controllers/Admin_my_module.php:

<?php
defined('BASEPATH') or exit('No direct script access allowed');

class Admin_my_module extends AdminController
{
    public function __construct()
    {
        parent::__construct();
    }

    public function index()
    {
        $data['title'] = 'My Module Admin Page';
        $this->load->view('my_module/admin/index', $data);
    }

    public function save_settings()
    {
        $CI = &get_instance();
        // Use models or libraries to save module-specific data
        $this->load->model('my_module/my_module_model');
        $this->my_module_model->save_settings($CI->input->post());
        set_alert('success', 'Settings saved.');
        redirect(admin_url('my_module'));
    }
}

f you want a client-side controller, you might do:

<?php
defined('BASEPATH') or exit('No direct script access allowed');

use app\services\ValidatesContact;

class Clients_my_module extends ClientsController
{
    use ValidatesContact;

    public function dashboard()
    {
        $data['my_info'] = 'Hello, client!';
        $this->load->view('my_module/public/index', $data);
    }
}

Note: If you extend ClientsController, you can use the ValidatesContact trait to ensure only logged-in contacts access controller methods.

5. Models

Models live in modules/my_module/models/, and you use them to interact with the database.

Example My_module_model.php:

<?php
defined('BASEPATH') or exit('No direct script access allowed');

class My_module_model extends CI_Model
{
    public function __construct()
    {
        parent::__construct();
    }

    public function save_settings($data)
    {
        // For example, store settings in perfex options table
        foreach ($data as $key => $value) {
            add_option('my_module_' . $key, $value);
        }
    }

    public function get_settings()
    {
        $settings = [];
        $settings['foo'] = get_option('my_module_foo');
        $settings['bar'] = get_option('my_module_bar');
        return $settings;
    }
}

Use Perfex helper functions like add_option(), get_option(), update_option() for storing module settings.

Prefix your option names (e.g., my_module_foo) to avoid conflicting with Perfex core or other modules.

6. Views

Your views folder should contain separate subfolders for admin and public (client side).

For example:

modules/my_module/views/
  admin/
    index.php
  public/
    index.php

Admin view (index): modules/my_module/views/admin/index.php:

<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<div class="panel_s">
  <div class="panel-body">
    <h4><?php echo $title; ?></h4>
    <form action="<?php echo admin_url('my_module/save_settings'); ?>" method="post">
      <div class="form-group">
        <label for="foo">Foo</label>
        <input type="text" name="foo" id="foo" value="<?php echo isset($settings['foo']) ? $settings['foo'] : ''; ?>" class="form-control">
      </div>
      <button type="submit" class="btn btn-primary">Save</button>
    </form>
  </div>
</div>

Public/Client view: modules/my_module/views/public/index.php:

<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<div class="my-module-client-dashboard">
  <h3>Welcome to My Module</h3>
  <p><?php echo $my_info; ?></p>
</div>

7. Language Files

To support translations, create language files under modules/my_module/language/[language]/.

For example: modules/my_module/language/english/my_module_lang.php

<?php

# English
$lang['my_module_title'] = 'My Module';
$lang['my_module_settings_saved'] = 'Settings have been saved successfully.';

Use your language variables in views and controllers to support internationalization.

Working With Hooks (Actions & Filters)

Perfex CRM provides hooks (similar to WordPress-style) that you can use to execute code at various points.

Some common hook functions:

hooks()->add_action('hook_name', 'my_function', $priority = 10);
hooks()->add_filter('filter_name', 'my_filter_function', $priority = 10, $accepted_args = 1);
hooks()->do_action('hook_name', $params = '');
hooks()->apply_filters('filter_name', $value, $params = []);

For example, to add a menu item when the admin panel loads:

hooks()->add_action('admin_init', function() {
    $CI = &get_instance();
    $CI->app_menu->add_sidebar_menu_item('my_module', [
        'name'     => 'My Module',
        'collapse' => false,
        'position' => 20,
        'href'     => admin_url('my_module'),
        'icon'     => 'fa fa-puzzle-piece',
    ]);
});

9. Module Options / Settings

As mentioned, use add_option(), get_option(), and update_option() to manage settings in the module. These store values in tbloptions table in Perfex database.

  • add_option($name, $value, $autoload) — add a new option.
  • get_option($name, $default = null) — retrieve an option.
  • update_option($name, $value) — update or create option if it doesn’t exist (from v2.3.3+).

10. Security Best Practices

When developing modules, you must ensure security:

  • Validate and sanitize all input data (e.g., from forms).
  • Use CodeIgniter’s built-in CSRF protection. Note: Perfex enables CSRF by default; ensure your forms work with it. Perfex CRM
  • Use defined('BASEPATH') or exit('No direct script access allowed'); at the top of your PHP files to prevent direct access.
  • Use parameterized queries when accessing the database (via CI’s Query Builder) to avoid SQL Injection.

11. Module Upgrading

When you release a new version of your module, you can handle upgrades via the init file version header. Perfex CRM compares the version in the init file with what’s already installed.

In the register_activation_hook, you can include logic to run database migrations or other setup for the new version.

Example:

function my_module_activate() {
    $CI = &get_instance();
    $CI->db->query("ALTER TABLE `" . db_prefix() . "my_module_table` ADD COLUMN `new_field` VARCHAR(255) NULL;");
}

12. Payment Gateway Module (Optional)

If you want to build a custom payment gateway module, Perfex provides support for this.

Steps:

  1. Create a folder /modules/my_gateway/libraries/ and a class named e.g. My_gateway_gateway.php. The filename must end with _gateway.php.
  2. In your init file, register the payment gateway:
register_payment_gateway('my_gateway', 'my_gateway');
  • In your gateway library class, define methods like process_payment($data) to handle the payment logic.
  • Optionally, create a controller under controllers/ for handling redirects or webhook callbacks.
  • If your gateway needs a webhook callback endpoint, you may need to exclude that URL from CSRF protection (since webhooks might POST data).

Example: Full “Simple Module” Walk‑Through

Putting it all together, here is a short walkthrough to build a very simple “Hello World” module.

  1. Create directory: modules/hello_world/
  2. Init File: hello_world.php:
<?php
defined('BASEPATH') or exit('No direct script access allowed');

/*
Module Name: Hello World
Description: A simple demo module for Perfex CRM.
Version: 1.0.0
Requires at least: 2.3.*
Author: Your Name
*/

register_activation_hook('hello_world', 'hello_world_activate');
register_deactivation_hook('hello_world', 'hello_world_deactivate');

function hello_world_activate() {
  // Module installed / activated
}

function hello_world_deactivate() {
  // Module deactivated
}

hooks()->add_action('admin_init', 'hello_world_add_menu');
function hello_world_add_menu() {
  $CI = &get_instance();
  $CI->app_menu->add_sidebar_menu_item('hello_world', [
    'name' => 'Hello World',
    'href' => admin_url('hello_world'),
    'position' => 50,
    'icon' => 'fa fa-smile'
  ]);
}

Controller: modules/hello_world/controllers/Admin_hello_world.php:

<?php
defined('BASEPATH') or exit('No direct script access allowed');

class Admin_hello_world extends AdminController
{
  public function index()
  {
    $data['title'] = _l('hello_world_title'); // use language
    $this->load->view('hello_world/admin/index', $data);
  }
}

Model: optional, but you could create Hello_world_model if needed.

View: modules/hello_world/views/admin/index.php:

<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<div class="panel_s">
  <div class="panel-body">
    <h4><?php echo $title; ?></h4>
    <p>Hello, this is my custom module!</p>
  </div>
</div>

Language: modules/hello_world/language/english/hello_world_lang.php:

<?php
$lang['hello_world_title'] = 'Hello World Module';

After you upload this module (zipped) into Setup → Modules in your Perfex admin UI, install and activate it, you should see a new “Hello World” menu item in the sidebar, and clicking it will display the simple page.

14. Testing & Debugging

Use development mode (enabled earlier) to catch PHP errors & deprecated function warnings.

Log via CodeIgniter’s logging (e.g., log_message('debug', 'my_module debug info');) for debugging.

Use browser dev tools to debug JS or view assets if your module includes CSS/JS.

Check database after activation if you’re creating tables or options, to ensure they exist.

15. Best Practices for Module Development

Best PracticeDescription
Prefix functions and classesPrevent naming collisions with core or other modules.
SecurityUse CSRF protection, sanitize inputs, prevent direct access.
Use hooksLeverage hooks()->add_action and hooks()->add_filter for clean integration.
VersioningUse module version in the init file and handle upgrades.
Separation of concernsKeep controllers, models, views, and libraries well organized.
InternationalizationUse language files for UI strings.
Maintain compatibilityTest module against new versions of Perfex, because major updates may break modules.

16. Advanced Use Cases

Payment Gateway Module: As covered, create a custom gateway by providing a library and controller.

Cron / Scheduled Tasks: You can register cron tasks in the init file to run background jobs.

Custom Libraries: Add PHP classes in libraries/ for business logic.

Language Packs: Add support for multiple languages by creating additional language subfolders.

Module Dependencies: If your module depends on other modules or libraries, check for their existence in activation hook.

17. Packaging & Distribution

Once your module is ready, zip the module folder.

The zip should preserve the structure under modules/my_module/....

Install via Perfex CRM Admin → Setup → Modules → Upload Module.

After uploading, click Install and then Activate. support.corbitaltech.dev

If you’re distributing (e.g., via CodeCanyon), clearly document version, requirements, and update process.

18. Troubleshooting Common Issues

ProblemPossible CauseSolution
Module not showing in listInit file name mismatch or header missingEnsure modules/<name>/<name>.php exists, headers are correct.
CSRF token errors on formCSRF protection is enabledUse CI form helper, include CSRF token, or adjust CSRF settings.
Controller 404Wrong class name or pathCheck controller file name, class extends correct base (AdminController or ClientsController).
Settings not savingOption keys wrongUse add_option / update_option with correctly prefixed names.
Security vulnerabilityUnsanitized inputValidate, sanitize, and escape inputs before database usage.

Conclusion

Developing a custom module in Perfex CRM gives you the flexibility to extend the CRM exactly as your business needs — whether you’re adding a simple admin page, integrating a payment gateway, or building a full client-facing feature. By following the modular structure, using CodeIgniter conventions, and leveraging Perfex’s hooks and helper functions, you can build robust, maintainable, and upgrade-friendly modules.

Here’s a quick recap:

  1. Set up your module directory under modules/.
  2. Create an init file with metadata and hooks.
  3. Build controllers, models, views, and optionally libraries & language files.
  4. Use add_option / get_option for settings.
  5. Secure your module, handle CSRF, and version your module.
  6. Package your module as a zip for installation.
  7. Test thoroughly, especially after Perfex updates.

FAQ’s About Perfex CRM Module Development:

1. What is a Perfex CRM module?

A module is an add-on that extends Perfex CRM with custom features.

Yes, Perfex allows full custom module development.

It is built with PHP (CodeIgniter framework).

Upload the module into the /modules/ directory.

Yes, you can create and extend API endpoints.

Yes, basic PHP, MVC, and CodeIgniter knowledge is required.

Use the main module init file e.g., mymodule.php.

Yes, using hooks like admin_init and admin_head.

Yes, each module includes activation and deactivation functions.

Yes, via the views/admin/ and views/public/ folders.

Yes, using migration files or manual schema creation.

Use $this->load->model('module_name/module_model');.

Yes, through assets and view loading functions.

Yes, via language/english/module_lang.php.

Enable CodeIgniter debugging or use log_message().

Yes, through custom libraries and controllers.

Yes, modules can include versioning and update scripts.

At the official docs: help.perfexcrm.com.

Yes, poorly written code can slow the CRM down.

Yes, many developers sell modules on marketplaces like CodeCanyon.

Scroll to Top