Conventions
PHP conventions
All PHP code in Paradigm CMS extensions follows strict conventions to keep extensions predictable, secure, and AI-friendly.
Procedural code only
Extension code is procedural. PHP classes are not used except in two places:
- The hook config class in
init.php(e.g.class extension_slug_config). PAPI_OBJECTsubclasses in theclasses/directory.
Everything else — request handlers, blocks, layouts, helpers — is written as plain procedural PHP.
Request handler pattern
Every file in API/requests/ must use the $request closure pattern. This is the
canonical shape for all API request handlers:
<?php
$request = function($props = false) {
// 1. Validate props exist
if ($props === false)
return array(
'success' => false,
'errors' => array(
array('message' => "We're missing information required to complete this request.")
)
);
// 2. Normalize props to object
if (!is_object($props))
$props = json_decode(json_encode($props));
// 3. Validate required properties
$requiredProperties = array('organization_ID', 'label');
foreach ($requiredProperties as $requiredProperty) :
if (!isset($props->$requiredProperty))
return array(
'success' => false,
'errors' => array(
array('message' => "We're missing information required to complete this request.")
)
);
endforeach;
// 4. Auth check
if (!defined('CURRENT_USER') || intval(CURRENT_USER['id']) === 0) :
return array(
'success' => false,
'errors' => array(
array('message' => "You must be logged in to make this request.")
)
);
endif;
// 5. Sanitize inputs
$props->organization_ID = intval($props->organization_ID);
$props->label = trim($props->label);
// 6. Perform action and return
$createResponse = PAPI_create_object('{ext}:{object}', array(
'organization_ID' => $props->organization_ID,
'label' => $props->label,
'slug' => PAPI_slugify($props->label),
'status' => 'active'
), 'internal');
if ($createResponse['success'] !== true) :
return $createResponse;
endif;
$item = PAPI_get_object('{ext}:{object}',
$createResponse['created_object']['id'], 'internal');
return array(
'success' => true,
'item' => $item
);
};
?>
Key rules for every request handler:
- Always use
$request = function($props = false) { ... };closure syntax. - Always normalize props:
if (!is_object($props)) $props = json_decode(json_encode($props)); - Always validate required properties with a
$requiredPropertiesarray andforeachloop. - Always sanitize inputs (
intval(),trim()) before use.
Authentication checks
Use one of three patterns depending on the access level needed.
Logged-in user check
if (!defined('CURRENT_USER') || intval(CURRENT_USER['id']) === 0) :
return array(
'success' => false,
'errors' => array(
array('message' => "You must be logged in to make this request.")
)
);
endif;
Site admin check
if (PAPI_is_user_logged_in() !== true || CURRENT_USER['is_admin'] !== true) {
return array(
'success' => false,
'errors' => array(
array('message' => 'You do not have permission to perform this action.')
)
);
}
Organization admin check
$orgAdminCheck = PAPI_request('organizations:is_organization_admin', array(
'organization_ID' => $props->organization_ID
));
if ($orgAdminCheck['success'] !== true && CURRENT_USER['is_admin'] !== true) :
return array(
'success' => false,
'errors' => array(
array('message' => 'You do not have permission to perform this action.')
)
);
endif;
Super admin check
if (PAPI_is_super_admin() !== true) {
return array(
'success' => false,
'errors' => array(
array('message' => 'You do not have permission to perform this action.')
)
);
}
CURRENT_USER constant
CURRENT_USER is a constant (array) available throughout all extension requests, blocks, and layouts.
It is not a variable — do not prefix it with $.
Available keys:
id— User ID (integer)email— Email addressfname— First namelname— Last nameis_admin— Boolean, site-level admin flagusers— Related user records
API response format
All API handlers return arrays with a success boolean.
Success response
return array(
'success' => true,
'items' => $items,
'totalPages' => $totalPages,
'currentPage' => $page
);
Error response
return array(
'success' => false,
'errors' => array(
array('message' => 'Descriptive error message here.')
)
);
Database helpers
PAPI_get_objects — retrieve multiple records
PAPI_get_objects(
'{extension_slug}:{object_slug}',
array(
'conditions' => array(
array(
array('{property}', '=', '{value}'),
array('{property2}', '=', '{value2}') // OR within same group
),
array(
array('{property3}', '=', '{value3}') // AND (separate group)
)
),
'orderBy' => array('{property}' => 'DESC'),
'limit' => 10,
'offset' => 0
),
'internal'
);
Conditions use nested arrays: inner arrays within a group are OR'd together, outer groups are AND'd together.
The third parameter is always 'internal' for internal calls.
PAPI_get_object — retrieve a single record
// By ID
$item = PAPI_get_object('{ext}:{object}', $id, 'internal');
// By conditions
$item = PAPI_get_object('{ext}:{object}', array(
array(array('slug', '=', $slug)),
array(array('status', '=', 'active'))
), 'internal');
Create, update, delete
// Create
$response = PAPI_create_object('{ext}:{object}', array(
'label' => $props->label,
'slug' => PAPI_slugify($props->label),
'status' => 'active'
), 'internal');
// Returns: array('success' => true, 'created_object' => array('id' => N))
// Update
$response = PAPI_update_object('{ext}:{object}', $id, array(
'label' => $props->label
), 'internal');
// Delete
$response = PAPI_delete_object('{ext}:{object}', $id, 'internal');
Calling other extension handlers
$response = PAPI_request('{ext}:{action}', array(
'param' => $value
));
JSON auto-decoding
Text fields containing valid JSON are automatically decoded into arrays when retrieved via PAPI helpers.
Never call json_decode() on retrieved object fields.
$item = PAPI_get_object('extension:object', $item_id, 'internal');
// If 'settings' contains '{"theme":"dark"}',
// $item['settings'] is already an array
Helper function scoping
If a helper function is needed, prefix its name with the full directory path to avoid naming conflicts across extensions:
function my_extension_1_0_0_src_API_requests_my_action_helperName() {
// Helper logic
}
Utility functions
PAPI_slugify($string)— Converts a string to a URL-friendly slug.PAPI_is_user_logged_in()— Returnstrueif a user session is active.PAPI_is_super_admin()— Returnstrueif the current user is a super admin.