This comprehensive guide covers extending ShahiLandin, custom integrations, and advanced development patterns for plugin developers.
Getting Started
Development Environment Setup
Requirements:
- Local WordPress installation (Local by Flywheel, XAMPP, or Docker)
- PHP 7.4+ with Xdebug (for debugging)
- Node.js 16+ and npm (for asset building)
- Composer (for dependency management)
- WP-CLI (for automation)
- Git (for version control)
- Visual Studio Code with PHP Intelephense extension
- PHPStorm
- Sublime Text with PHP packages
- Namespace Organization: All classes use the
ShahiLandinnamespace - Service Layer: Business logic in service classes (e.g.,
Landing_Service) - Single Responsibility: Each class has one clear purpose
- Dependency Injection: Services receive dependencies via constructor
- Hook-Based: Extensible via WordPress actions and filters
{theme}/shahilandin/single-landing-{slug}.php{theme}/shahilandin/single-landing-{id}.php{theme}/shahilandin/single-landing.php{plugin}/templates/single-shahi-landing.php- WordPress Coding Standards: https://developer.wordpress.org/coding-standards/
- WordPress Plugin Handbook: https://developer.wordpress.org/plugins/
- REST API Handbook: https://developer.wordpress.org/rest-api/
- WP-CLI Documentation: https://developer.wp-cli.org/
- PHPUnit Documentation: https://phpunit.de/documentation.html
Recommended IDE:
Plugin Structure
`
ShahiLandin/
├── assets/ # Frontend assets
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript files
│ └── images/ # Image assets
├── includes/ # Core PHP classes
│ ├── admin/ # Admin-specific classes
│ ├── api/ # REST API controllers
│ ├── cli/ # WP-CLI commands
│ ├── frontend/ # Frontend classes
│ ├── modules/ # Feature modules
│ ├── post-types/ # Custom post type definitions
│ ├── services/ # Business logic services
│ ├── class-plugin.php # Main plugin class
│ ├── class-options.php # Options management
│ └── helpers.php # Helper functions
├── templates/ # Template files
├── languages/ # Translation files
├── tests/ # PHPUnit tests
├── shahilandin.php # Plugin bootstrap file
└── uninstall.php # Uninstall cleanup
`
Debugging
Enable WordPress debug mode in wp-config.php:
`php
define(‘WP_DEBUG’, true);
define(‘WPDEBUGLOG’, true);
define(‘WPDEBUGDISPLAY’, false);
define(‘SCRIPT_DEBUG’, true);
`
Log to file:
`php
errorlog(‘Debug message: ‘ . printr($data, true));
`
Use Xdebug breakpoints in your IDE for step-by-step debugging.
Core Concepts
Plugin Architecture
ShahiLandin follows these architectural patterns:
Custom Post Type
Landing pages use the shahi_landing post type:
`php
use ShahiLandin\PostType\LandingPost_Type;
// Get post type name
$posttype = LandingPostType::POSTTYPE; // ‘shahi_landing’
// Register additional meta
add_action(‘init’, function() {
registerpostmeta(‘shahilanding’, ‘custom_meta’, [
‘type’ => ‘string’,
‘single’ => true,
‘showinrest’ => true,
]);
});
`
Services
Services encapsulate business logic:
`php
use ShahiLandin\Services\Landing_Service;
// Create landing page
$postid = LandingService::create_landing([
‘post_title’ => ‘My Landing Page’,
‘post_status’ => ‘publish’,
‘html_content’ => ‘
‘,
‘css_content’ => ‘.custom { color: red; }’
]);
// Get statistics
$stats = LandingService::getstatistics($post_id);
// Duplicate landing page
$newid = LandingService::duplicatelanding($postid);
`
Meta Boxes
Landing pages use custom meta boxes for HTML, CSS, and settings:
`php
use ShahiLandin\Admin\Meta_Boxes;
// Meta keys
$html = getpostmeta($postid, MetaBoxes::HTMLMETAKEY, true);
$css = getpostmeta($postid, MetaBoxes::CSSMETAKEY, true);
$rendermode = getpostmeta($postid, MetaBoxes::RENDERMODEMETAKEY, true);
`
Custom Integrations
External Analytics Integration
Integrate with third-party analytics platforms:
`php
MixpanelIntegration {
private $token;
public function __construct() {
$this->token = getoption(‘shahilandinmixpanel_token’);
if ($this->token) {
addaction(‘shahilandinviewtracked’, [$this, ‘trackview’], 10, 2);
addaction(‘shahilandinconversiontracked’, [$this, ‘trackconversion’], 10, 3);
}
}
public function trackview($postid, $data) {
$this->send_event(‘Landing Page Viewed’, [
‘Landing ID’ => $post_id,
‘Landing Title’ => getthetitle($post_id),
‘Referrer’ => $data[‘referrer’] ?? ”,
‘URL’ => getpermalink($postid)
]);
}
public function trackconversion($postid, $goal, $data) {
$this->send_event(‘Landing Page Conversion’, [
‘Landing ID’ => $post_id,
‘Landing Title’ => getthetitle($post_id),
‘Goal’ => $goal,
‘Conversion Data’ => $data
]);
}
private function sendevent($eventname, $properties) {
$event = [
‘event’ => $event_name,
‘properties’ => array_merge($properties, [
‘token’ => $this->token,
‘time’ => time()
])
];
$url = ‘https://api.mixpanel.com/track/’;
wpremotepost($url, [
‘body’ => [
‘data’ => base64encode(jsonencode($event))
],
‘timeout’ => 5
]);
}
}
new ShahiLandinMixpanelIntegration();
`
CRM Integration
Sync conversions with your CRM:
`php
HubSpotSync {
private $api_key;
public function __construct() {
$this->apikey = getoption(‘hubspotapikey’);
addaction(‘shahilandinconversiontracked’, [$this, ‘syncto_hubspot’], 10, 3);
}
public function synctohubspot($post_id, $goal, $data) {
// Only sync email signups
if ($goal !== ‘newsletter_signup’ || empty($data[’email’])) {
return;
}
$contact_data = [
‘properties’ => [
’email’ => $data[’email’],
‘landingpageid’ => $post_id,
‘landingpagetitle’ => getthetitle($post_id),
‘signup_source’ => ‘shahilandin’,
‘signup_date’ => date(‘Y-m-d H:i:s’)
]
];
$response = wpremotepost(‘https://api.hubapi.com/crm/v3/objects/contacts’, [
‘headers’ => [
‘Authorization’ => ‘Bearer ‘ . $this->api_key,
‘Content-Type’ => ‘application/json’
],
‘body’ => jsonencode($contactdata),
‘timeout’ => 10
]);
if (iswperror($response)) {
errorlog(‘HubSpot sync failed: ‘ . $response->geterror_message());
}
}
}
new ShahiLandinHubSpotSync();
`
Email Marketing Integration
Connect to email marketing platforms:
`php
MailchimpIntegration {
private $api_key;
private $list_id;
public function __construct() {
$this->apikey = getoption(‘mailchimpapikey’);
$this->listid = getoption(‘mailchimplistid’);
addaction(‘shahilandinconversiontracked’, [$this, ‘addsubscriber’], 10, 3);
}
public function addsubscriber($postid, $goal, $data) {
if ($goal !== ‘newsletter_signup’ || empty($data[’email’])) {
return;
}
$dc = substr($this->apikey, strpos($this->apikey, ‘-‘) + 1);
$url = “https://{$dc}.api.mailchimp.com/3.0/lists/{$this->list_id}/members/”;
$member_data = [
’email_address’ => $data[’email’],
‘status’ => ‘subscribed’,
‘merge_fields’ => [
‘FNAME’ => $data[‘first_name’] ?? ”,
‘LNAME’ => $data[‘last_name’] ?? ”,
‘LANDINGID’ => $post_id
],
‘tags’ => [‘shahilandin’, getthetitle($post_id)]
];
wpremotepost($url, [
‘headers’ => [
‘Authorization’ => ‘Basic ‘ . base64encode(‘user:’ . $this->apikey),
‘Content-Type’ => ‘application/json’
],
‘body’ => jsonencode($memberdata)
]);
}
}
new ShahiLandinMailchimpIntegration();
`
Custom Modules
Creating a Module
Modules extend ShahiLandin with new features:
`php
action(‘adminmenu’, [self::class, ‘register_menu’]);
addaction(‘adminenqueuescripts’, [self::class, ‘enqueueassets’]);
addaction(‘restapiinit’, [self::class, ‘registerrest_routes’]);
}
private static function is_enabled() {
return (bool) getoption(‘shahilandincustomfeatureenabled’, false);
}
public static function register_menu() {
addsubmenupage(
‘edit.php?posttype=shahilanding’,
__(‘Custom Feature’, ‘shahilandin’),
__(‘Custom Feature’, ‘shahilandin’),
‘manageshahilandings’,
‘shahilandin-custom-feature’,
[self::class, ‘render_page’]
);
}
public static function render_page() {
?>
htmle(‘Custom Feature’, ‘shahilandin’); ?>
htmle(‘Your custom feature interface here.’, ‘shahilandin’); ?>
enqueuescript(
‘shahilandin-custom-feature’,
SHAHILANDIN_URL . ‘assets/js/custom-feature.js’,
[‘jquery’],
SHAHILANDIN_VERSION,
true
);
}
public static function registerrestroutes() {
registerrestroute(‘shahilandin/v1’, ‘/custom-feature’, [
‘methods’ => ‘GET’,
‘callback’ => [self::class, ‘restgetdata’],
‘permission_callback’ => function() {
return currentusercan(‘manageshahilandings’);
}
]);
}
public static function restgetdata(\WPRESTRequest $request) {
return new \WPRESTResponse([
‘data’ => ‘Custom feature data’
]);
}
}
`
Register the module in class-plugin.php:
`php
private function register_hooks(): void {
// … existing code …
addaction(‘init’, [$this, ‘bootstrapmodules’]);
}
public function bootstrap_modules(): void {
TemplateBuilderModule::bootstrap();
TemplatesProModule::bootstrap();
\ShahiLandin\Modules\CustomFeature\Module::bootstrap(); // Add your module
}
`
Custom Templates
Creating Custom Landing Templates
Override default templates:
`php
id = getthe_ID();
$html = getpostmeta($postid, MetaBoxes::HTMLMETAKEY, true);
$css = getpostmeta($postid, MetaBoxes::CSSMETAKEY, true);
?>
`
Template hierarchy:
Custom REST Endpoints
Extending the REST API
Add custom endpoints to the REST API:
`php
CustomAPI {
public function __construct() {
addaction(‘restapiinit’, [$this, ‘registerroutes’]);
}
public function register_routes() {
// Get landing pages by tag
registerrestroute(‘shahilandin/v1’, ‘/landings/by-tag/(?P
‘methods’ => ‘GET’,
‘callback’ => [$this, ‘getlandingsby_tag’],
‘permission_callback’ => function() {
return currentusercan(‘editshahilandings’);
}
]);
// Bulk update landing pages
registerrestroute(‘shahilandin/v1’, ‘/landings/bulk-update’, [
‘methods’ => ‘POST’,
‘callback’ => [$this, ‘bulkupdatelandings’],
‘permission_callback’ => function() {
return currentusercan(‘editshahilandings’);
},
‘args’ => [
‘ids’ => [
‘required’ => true,
‘type’ => ‘array’,
‘items’ => [‘type’ => ‘integer’]
],
‘status’ => [
‘type’ => ‘string’,
‘enum’ => [‘publish’, ‘draft’, ‘pending’]
]
]
]);
// Export landing page
registerrestroute(‘shahilandin/v1’, ‘/landings/(?P
‘methods’ => ‘GET’,
‘callback’ => [$this, ‘export_landing’],
‘permission_callback’ => function() {
return currentusercan(‘editshahilandings’);
}
]);
}
public function getlandingsby_tag($request) {
$tag = $request->get_param(‘tag’);
$args = [
‘posttype’ => ‘shahilanding’,
‘tax_query’ => [
[
‘taxonomy’ => ‘shahilandingtag’,
‘field’ => ‘slug’,
‘terms’ => $tag
]
]
];
$posts = get_posts($args);
$data = [];
foreach ($posts as $post) {
$data[] = [
‘id’ => $post->ID,
‘title’ => $post->post_title,
‘link’ => get_permalink($post->ID)
];
}
return new \WPRESTResponse($data);
}
public function bulkupdatelandings($request) {
$ids = $request->get_param(‘ids’);
$status = $request->get_param(‘status’);
$updated = [];
foreach ($ids as $id) {
if (currentusercan(‘editshahilanding’, $id)) {
wpupdatepost([
‘ID’ => $id,
‘post_status’ => $status
]);
$updated[] = $id;
}
}
return new \WPRESTResponse([
‘updated’ => $updated,
‘count’ => count($updated)
]);
}
public function export_landing($request) {
$postid = (int) $request->getparam(‘id’);
$post = getpost($postid);
if (! $post || $post->posttype !== ‘shahilanding’) {
return new \WPError(‘notfound’, ‘Landing page not found’, [‘status’ => 404]);
}
$html = getpostmeta($postid, ‘shahilandin_html’, true);
$css = getpostmeta($postid, ‘shahilandin_css’, true);
$export = [
‘title’ => $post->post_title,
‘slug’ => $post->post_name,
‘html’ => $html,
‘css’ => $css,
‘meta’ => getpostmeta($post_id),
‘exportedat’ => currenttime(‘Y-m-d H:i:s’)
];
return new \WPRESTResponse($export);
}
}
new ShahiLandinCustomAPI();
`
Custom WP-CLI Commands
Extending WP-CLI
Add custom WP-CLI commands:
`php
CLI’) || ! WPCLI) {
return;
}
class ShahiLandinCustomCLI {
/**
* Optimize all landing pages.
*
* ## EXAMPLES
*
* wp shahilandin-custom optimize
*/
public function optimize($args, $assoc_args) {
$landings = get_posts([
‘posttype’ => ‘shahilanding’,
‘post_status’ => ‘publish’,
‘numberposts’ => -1
]);
$count = 0;
foreach ($landings as $post) {
$html = getpostmeta($post->ID, ‘shahilandinhtml’, true);
$css = getpostmeta($post->ID, ‘shahilandincss’, true);
// Minify HTML
$html = preg_replace(‘/\s+/’, ‘ ‘, $html);
updatepostmeta($post->ID, ‘shahilandinhtml’, $html);
// Minify CSS
$css = preg_replace(‘/\s+/’, ‘ ‘, $css);
$css = preg_replace(‘/\s([{}|:;,])\s/’, ‘$1’, $css);
updatepostmeta($post->ID, ‘shahilandincss’, $css);
$count++;
\WP_CLI::log(“Optimized landing page {$post->ID}”);
}
\WP_CLI::success(“Optimized {$count} landing pages.”);
}
/**
* Generate performance report.
*
* ## EXAMPLES
*
* wp shahilandin-custom performance-report
*/
public function performancereport($args, $assocargs) {
$landings = get_posts([
‘posttype’ => ‘shahilanding’,
‘post_status’ => ‘publish’,
‘numberposts’ => -1
]);
$report = [];
foreach ($landings as $post) {
$html = getpostmeta($post->ID, ‘shahilandinhtml’, true);
$css = getpostmeta($post->ID, ‘shahilandincss’, true);
$report[] = [
‘ID’ => $post->ID,
‘Title’ => $post->post_title,
‘HTML Size (KB)’ => round(strlen($html) / 1024, 2),
‘CSS Size (KB)’ => round(strlen($css) / 1024, 2),
‘Total Size (KB)’ => round((strlen($html) + strlen($css)) / 1024, 2)
];
}
\WPCLI\Utils\formatitems(‘table’, $report, array_keys($report[0]));
}
}
\WPCLI::addcommand(‘shahilandin-custom’, ‘ShahiLandinCustomCLI’);
`
Testing
PHPUnit Tests
Create unit tests for your extensions:
`php
CustomTest extends WP_UnitTestCase {
private $post_id;
public function setUp(): void {
parent::setUp();
// Create a test landing page
$this->postid = wpinsert_post([
‘posttype’ => ‘shahilanding’,
‘post_title’ => ‘Test Landing Page’,
‘post_status’ => ‘publish’
]);
updatepostmeta($this->postid, ‘shahilandin_html’, ‘
‘);
updatepostmeta($this->postid, ‘shahilandin_css’, ‘.test { color: red; }’);
}
public function tearDown(): void {
wpdeletepost($this->post_id, true);
parent::tearDown();
}
public function testlandingpage_created() {
$post = getpost($this->postid);
$this->assertEquals(‘shahilanding’, $post->posttype);
$this->assertEquals(‘Test Landing Page’, $post->post_title);
}
public function testlandingpage_meta() {
$html = getpostmeta($this->postid, ‘shahilandin_html’, true);
$css = getpostmeta($this->postid, ‘shahilandin_css’, true);
$this->assertEquals(‘
‘, $html);
$this->assertEquals(‘.test { color: red; }’, $css);
}
public function testlandingservice_stats() {
$stats = \ShahiLandin\Services\LandingService::getstatistics($this->post_id);
$this->assertIsArray($stats);
$this->assertArrayHasKey(‘views’, $stats);
$this->assertArrayHasKey(‘conversions’, $stats);
}
}
`
Run tests:
`bash
phpunit tests/test-custom.php
`
Performance Best Practices
Optimize Database Queries
`php
// ❌ Bad: N+1 query problem
$landings = getposts([‘posttype’ => ‘shahi_landing’]);
foreach ($landings as $landing) {
$views = getpostmeta($landing->ID, ‘shahilandinviews’, true);
}
// ✅ Good: Single query with meta
$landings = get_posts([
‘posttype’ => ‘shahilanding’,
‘meta_query’ => [
[
‘key’ => ‘shahilandinviews’,
‘compare’ => ‘EXISTS’
]
]
]);
`
Use Transients for Caching
`php
function getlandingstatscached($postid) {
$cachekey = “landingstats{$postid}”;
$stats = gettransient($cachekey);
if (false === $stats) {
$stats = \ShahiLandin\Services\LandingService::getstatistics($post_id);
settransient($cachekey, $stats, HOURINSECONDS);
}
return $stats;
}
// Clear cache when data changes
addaction(‘shahilandinlandingupdated’, function($postid) {
deletetransient(“landingstats{$postid}”);
});
`
Lazy Load Heavy Operations
`php
// ❌ Bad: Load everything upfront
addaction(‘admininit’, function() {
$alllandings = getposts([‘posttype’ => ‘shahilanding’, ‘numberposts’ => -1]);
// Process all landings…
});
// ✅ Good: Load on-demand
addaction(‘adminmenu’, function() {
addsubmenupage(
‘edit.php?posttype=shahilanding’,
‘Reports’,
‘Reports’,
‘manageshahilandings’,
‘shahilandin-reports’,
function() {
// Only load when page is accessed
$landings = getposts([‘posttype’ => ‘shahi_landing’]);
// …
}
);
});
`
Security Best Practices
Sanitize and Validate Input
`php
// Sanitize text input
$title = sanitizetextfield($_POST[‘title’]);
// Sanitize HTML
$html = wpksespost($_POST[‘html’]);
// Validate integer
$postid = absint($POST[‘post_id’]);
// Validate URL
$url = escurlraw($_POST[‘url’]);
// Validate email
$email = sanitizeemail($POST[’email’]);
`
Check Capabilities
`php
// Check if user can create landing pages
if (! currentusercan(‘createshahilandings’)) {
wp_die(‘Insufficient permissions’);
}
// Check if user can edit specific landing page
if (! currentusercan(‘editshahilanding’, $post_id)) {
wp_die(‘You cannot edit this landing page’);
}
`
Use Nonces
`php
// Generate nonce
wpnoncefield(‘shahilandinsavemeta’, ‘shahilandinmetanonce’);
// Verify nonce
if (! isset($POST[‘shahilandinmeta_nonce’]) ||
! wpverifynonce($POST[‘shahilandinmetanonce’], ‘shahilandinsave_meta’)) {
return;
}
`
Troubleshooting
Enable Debug Mode
`php
// In wp-config.php
define(‘SHAHILANDIN_DEBUG’, true);
// In your code
if (defined(‘SHAHILANDINDEBUG’) && SHAHILANDINDEBUG) {
errorlog(‘Debug info: ‘ . printr($data, true));
}
`
Common Issues
Problem: Changes not appearing
Solution: Clear all caches (plugin, theme, server, CDN)
Problem: REST API returns 401
Solution: Check authentication, regenerate application passwords
Problem: WP-CLI command not found
Solution: Verify plugin is active, check namespace and class name
Problem: Hook not firing
Solution: Verify action/filter name, check priority and parameter count
Additional Resources
—
For hooks and filters reference, see the Hooks & Filters Reference article.
Share this article
Still need help?
Our support team is ready to assist you with personalized guidance for your workspace.