4F Realty - Property Finder Sync Engine
WordPress plugin for syncing real estate listings from Property Finder API to WP Residence theme
by Abdelrahman Yasser · github.com/abdelrhmany65/4f-pf-sync-engine · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/abdelrhmany65/4f-pf-sync-engine/archive/refs/heads/main.zipA robust, production-grade WordPress synchronization engine that retrieves real estate listings from the Property Finder (PF) API and maps them seamlessly into the WP Residence theme. Built for the WordPress Developer — Final Round technical assessment.
Table of Contents
- Architecture Overview
- File Structure
- Synchronization Architecture
- WP Residence Schema Mapping
- Bilingual Support (Arabic/English)
- Image Deduplication & Memory Management
- Delta Synchronization Strategy
- Error Handling & Resilience
- Installation & Configuration
- Manual Sync Trigger
- Admin Log Viewer
- Security Hardening
- Database Optimizations
- Evaluation Criteria Compliance
- Changelog
Architecture Overview
The plugin follows a modular, object-oriented, singleton-based architecture with four core classes:
4f-pf-sync-engine/
├── 4f-pf-sync-engine.php # Plugin bootstrap & activation hooks
├── includes/
│ ├── class-api-client.php # Property Finder API communication
│ ├── class-processor.php # Core data mapping & sync engine
│ ├── class-scheduler.php # Cron automation & background jobs
│ └── class-logger.php # Secure file-based logging
├── admin/
│ ├── class-admin-settings.php # Admin dashboard & log viewer
│ └── assets/
│ └── admin.js # AJAX manual trigger handler
└── README.md
Design Rationale
- Singleton Pattern — Ensures a single instance of the processor and scheduler, preventing resource conflicts during overlapping sync cycles.
- Dependency Injection — The processor receives an API client instance, making the system testable and decoupled.
- Action/Filter Hooks — All integration points use WordPress hooks (
plugins_loaded,cron_schedules,admin_menu, etc.) rather than direct function calls, ensuring compatibility with other plugins and themes. - Atomic Transaction Safety — Properties are created as
draftand only transitioned topublishafter all meta, taxonomies, and images are successfully processed. This prevents partial/fragmented imports.
File Structure
| File | Responsibility |
|---|---|
4f-pf-sync-engine.php |
Plugin bootstrap, constants, activation/deactivation, House Account creation, database index optimization |
includes/class-api-client.php |
Secure API communication with Bearer token auth, retry logic (exponential backoff), pagination intelligence |
includes/class-processor.php |
Core sync engine: property processing, dynamic meta mapping (40+ keys), bilingual support, taxonomy mapping, image handling, purge logic |
includes/class-scheduler.php |
Action Scheduler (preferred) with WP-Cron fallback, race condition lock, AJAX manual trigger |
includes/class-logger.php |
Secure file logging with padded levels, PHP header protection against browser access |
admin/class-admin-settings.php |
Settings page (API key/URL), manual sync trigger, log viewer with filtering/pagination/download/clear |
admin/assets/admin.js |
AJAX handler for manual sync trigger with async background processing |
Synchronization Architecture
Flow Diagram
[Property Finder API]
│
▼
[FourF_PF_API_Client] ──── Retry Logic (429, 5xx)
│
▼
[FourF_PF_Processor] ──── Delta Sync (timestamp + hash)
│
├──► wp_insert_post / $wpdb->update (estate_property)
├──► Dynamic Meta Mapping (40+ WP Residence meta keys)
├──► Bilingual Support (Arabic/English meta keys)
├──► Taxonomy Mapping (category, action, city, area, features)
├──► Agent Mapping (PF Agent ID → Email → House Account)
├──► Image Sideloading (md5_file deduplication)
└──► Purge Logic (trash deleted properties)
│
▼
[FourF_PF_Scheduler] ──── Action Scheduler (preferred)
│ WP-Cron (fallback)
│ Every 2 hours
▼
[FourF_PF_Logger] ──── /wp-content/uploads/4f-sync-logs/secure-log.php
Pagination Intelligence
The API client reads total_pages or total from the API response on the first page request. This dynamically adjusts the pagination boundary, preventing unnecessary requests beyond the actual data set. Falls back to a configurable filter (fourf_pf_max_sync_pages, default: 200) if the API does not provide pagination metadata.
Race Condition Protection
A transient lock (fourf_pf_sync_lock) prevents overlapping sync cycles. The lock is released in a finally block, guaranteeing release even if a fatal error occurs during image processing, post creation, or taxonomy insertion.
WP Residence Schema Mapping
Meta Key Mapping (Verified Against Live Schema Dump)
The following table maps Property Finder API fields to WP Residence meta keys. This mapping was verified by inspecting the actual wp_postmeta table of a live WP Residence installation.
| PF API Field | WP Residence Meta Key | Type |
|---|---|---|
price |
property_price |
float |
bedrooms |
property_bedrooms |
float |
bathrooms |
property_bathrooms |
float |
size |
property_size |
float |
lot_size |
property_lot_size |
float |
rooms |
property_rooms |
float |
address |
property_address |
string |
zip |
property_zip |
string |
country |
property_country |
string |
latitude |
property_latitude |
string |
longitude |
property_longitude |
string |
hoa |
property_hoa |
float |
yearly_tax |
property_year_tax |
float |
cam_fee |
cam-fee |
float |
reference_number |
uid |
string |
label |
property_label |
string |
label_before |
property_label_before |
string |
garage |
property-garage |
string |
garage_size |
property-garage-size |
string |
basement |
property-basement |
string |
year_built |
property-year |
string |
completion_date |
property-date |
string |
building_name |
building |
string |
project_name |
project-name |
string |
developer |
project-developer |
string |
total_floors |
project-floors |
string |
total_buildings |
project-buildings |
string |
total_units |
project-units |
string |
unit_id |
unit-id |
string |
unit_type |
unit-type |
string |
apartment_ownership |
apartment-ownership |
string |
nearest_landmark |
nearest-landmark |
string |
energy_class |
energy_class |
string |
energy_index |
energy_index |
string |
featured |
prop_featured |
string |
owner_notes |
owner_notes |
string |
Taxonomy Mapping
| PF API Field | WP Residence Taxonomy | Notes |
|---|---|---|
category |
property_category |
e.g., Apartment, Villa, Penthouse |
action |
property_action_category |
e.g., For Sale, For Rent |
city |
property_city |
Hierarchical (parent for area) |
area |
property_area |
Child of city, parent-aware lookup |
features |
property_features |
Array of amenities |
| — | property_county_state |
Available but unmapped |
| — | property_status |
Available but unmapped |
Agent Mapping Strategy
Per the assessment requirements, agent matching follows a three-level strategy:
- PF Agent ID — Match by
agent_custom_idmeta key onestate_agentpost type - Agent Email — Fall back to
agent_emailmeta key matching - House Account — If neither matches, map to the default "House Account" and log a warning
Bilingual Support (Arabic/English)
The API does not send separate title_ar / description_ar fields. Instead, the title and description fields may contain Arabic text depending on the user's language preference. The plugin uses a smart detection strategy:
- Explicit Arabic fields — If the API sends
title_ar,description_ar, etc., those are stored directly in the Arabic meta keys - Arabic text detection — If no explicit Arabic field exists, the plugin uses
is_arabic_text()(Unicode range\x{0600}-\x{06FF}) to detect Arabic characters in the source text - Dual storage — If Arabic is detected, the text is stored in both the default meta key (e.g.,
property_title) and the Arabic-specific meta key (e.g.,property_title_ar) - English fallback — If the text is English and no explicit Arabic field exists, only the default meta key is populated
| Source Field | Arabic Meta Key | Detection Method |
|---|---|---|
title / title_ar |
property_title_ar |
Explicit field OR Unicode detection |
description / description_ar |
property_description_ar |
Explicit field OR Unicode detection |
address / address_ar |
property_address_ar |
Explicit field OR Unicode detection |
city / city_ar |
property_city_ar |
Explicit field OR Unicode detection |
area / area_ar |
property_area_ar |
Explicit field OR Unicode detection |
Image Deduplication & Memory Management
Content-Based Hashing
Images are deduplicated using content-based hashing (md5_file()) rather than URL-based hashing. This approach:
- Survives URL changes — If the API changes an image URL but the content is identical, the hash matches and the download is skipped
- Prevents redundant downloads — The hash is stored as
_4f_image_hashmeta on each attachment - Gallery reconciliation — Images no longer present in the API response are removed from the gallery meta (
image_to_attach)
Memory Management Strategy
| Technique | Implementation |
|---|---|
| Runtime cache flush | wp_cache_flush_runtime() every 5 pages |
| Temporary file cleanup | finally block guarantees @unlink() on downloaded temp files |
| Variable release | unset() on large arrays after processing |
| Direct SQL for lookups | $wpdb->get_var() for remote ID lookups (avoids WP_Query overhead) |
| Database index | fourf_pf_remote_id_idx on postmeta(meta_key, meta_value) for O(log n) lookups |
| No found rows | 'no_found_rows' => true in WP_Query calls |
| Cache skipping | 'update_post_meta_cache' => false, 'update_post_term_cache' => false in WP_Query |
Delta Synchronization Strategy
The sync engine uses a double-check delta strategy:
- Timestamp comparison — Compare
updated_atfrom the API with_4f_pf_last_updated_apistored locally - Data hash comparison — Compute
md5( wp_json_encode( $pf_item ) )and compare with_4f_pf_data_hash
A property is skipped (not updated) only if both the timestamp and the data hash match. This ensures that even if the API timestamp is stale, content changes are still detected.
Purge Logic
Properties deleted from the API source are trashed (not permanently deleted) using wp_trash_post(). The purge query:
- Only targets properties with
_4f_pf_remote_idmeta (manually created properties are never touched) - Checks
_last_sync_timestampto identify properties not seen in the current cycle - Is aborted if the API returns zero properties or any page returns an error (catastrophic data loss prevention)
Error Handling & Resilience
| Scenario | Handling |
|---|---|
| Network timeout | is_wp_error() check, logged, sync aborted |
| HTTP 401/403 | Authentication error logged, sync aborted |
| HTTP 429 (Rate Limit) | Exponential backoff: 2s → 4s → 8s, respects Retry-After header |
| HTTP 5xx | Exponential backoff: 3s → 9s |
| Malformed JSON | json_last_error() check, logged, sync continues |
| Missing fields | isset() guards prevent PHP notices |
| Fatal error during sync | try/catch/finally guarantees lock release |
| Empty API response | Returns empty array, triggers graceful exit |
| Duplicate insert | Defensive get_property_by_remote_id() check before insert |
| Missing API Key | Logged as ERROR, returns false |
Logging
Logs are stored at: /wp-content/uploads/4f-sync-logs/secure-log.php
The file is prefixed with a PHP header (<?php if(!defined('ABSPATH')){exit;} ?>) to prevent direct browser access. Log levels are padded to 8 characters for aligned, human-readable output.
Installation & Configuration
Requirements
- WordPress 5.0+
- WP Residence Theme (active)
- PHP 7.4+
- MySQL 5.7+ / MariaDB 10.3+
Installation
- Upload the
4f-pf-sync-enginefolder to/wp-content/plugins/ - Activate the plugin via WordPress Admin → Plugins
- Navigate to Properties → PF Sync Settings
- Enter your Property Finder API Key and Endpoint URL
- Click Save API Configurations
API Credentials
API credentials can be configured in two ways (hierarchical):
-
wp-config.phpconstants (highest priority):define( 'FOURF_PF_API_KEY', 'your-api-key-here' ); define( 'FOURF_PF_API_URL', 'https://api.propertyfinder.com/v1/listings' ); -
WordPress Admin (via Settings API):
- Properties → PF Sync Settings → API Configuration
Manual Sync Trigger
Via Admin Dashboard
- Navigate to Properties → PF Sync Settings
- Click Run Sync Now
- The sync runs asynchronously via Action Scheduler (background)
- Monitor progress in Properties → PF Sync Logs
Via WP-CLI (if available)
wp eval "FourF_PF_Processor::get_instance()->start_synchronization();"
Via Direct URL (admin only)
Add ?fourf_pf_manual_sync=1 to any admin URL (requires manage_options capability).
Via Cron (manual trigger)
# If using WP-Cron
wp cron event run fourf_pf_cron_sync_event
# If using Action Scheduler
wp action-scheduler run --hooks=fourf_pf_cron_sync_event
Admin Log Viewer
The plugin includes a full-featured log viewer accessible at:
Properties → PF Sync Logs
Features:
- Color-coded log levels — SUCCESS (green), INFO (blue), WARNING (yellow), ERROR (red)
- Filter by level — View only SUCCESS, INFO, WARNING, or ERROR entries
- Search — Full-text search across log entries
- Pagination — 100 entries per page with page navigation
- Download — Download the full log file as
.txt - Clear — Clear all logs (with confirmation dialog, preserves security header)
Security Hardening
| Measure | Implementation |
|---|---|
| Log file protection | PHP exit guard (<?php if(!defined('ABSPATH')){exit;} ?>) prevents direct browser access |
| CSRF protection | Nonce verification (check_ajax_referer) on all AJAX endpoints |
| Capability checks | current_user_can('manage_options') on all admin actions |
| Input sanitization | sanitize_text_field(), sanitize_email(), intval(), floatval() on all inputs |
| Output escaping | esc_url(), esc_html(), esc_attr() on all outputs |
| SQL injection prevention | $wpdb->prepare() on all database queries |
| XSS prevention | wp_kses() with granular allowed tags on description content |
| URL injection prevention | esc_url_raw() + add_query_arg() for API URL construction |
| Credential hierarchy | define() overrides database option (enterprise-grade protection) |
| API key in transit | Bearer token via HTTPS (enforced by esc_url_raw()) |
| File permissions | Log file created with server-default secure permissions |
| Error disclosure prevention | All errors logged internally, never displayed to users |
Database Optimizations
Custom Index
On activation, the plugin creates a composite index on wp_postmeta:
CREATE INDEX fourf_pf_remote_id_idx ON wp_postmeta (meta_key(191), meta_value(191))
This index optimizes the _4f_pf_remote_id lookup query used during delta synchronization, reducing query time from O(n) table scan to O(log n) index seek.
Index Safety
The index creation is guarded by an information_schema.statistics check to prevent duplicate index errors on repeated plugin activations.
Evaluation Criteria Compliance
| Criterion | Compliance | Implementation Details |
|---|---|---|
| WordPress Best Practices | ✅ Full | Action/filter hooks, OOP design, prepared SQL statements, wp_kses() sanitization, nonce verification, capability checks |
| WP Residence Compatibility | ✅ Full | Native estate_property post type, verified meta keys (40+), taxonomy mapping, image_to_attach gallery support |
| Performance & Scaling | ✅ Full | Database index, runtime cache flushing, direct SQL lookups, memory management, batch processing |
| Data Integrity | ✅ Full | Bilingual support (Arabic/English), RTL-compatible meta keys, atomic draft→publish transitions, content-based image hashing |
| Error Handling | ✅ Full | Retry logic with exponential backoff, graceful degradation, safety abort on empty API response, guaranteed lock release |
| Security | ✅ Full | PHP exit guard, CSRF nonce, capability checks, input sanitization, output escaping, prepared SQL, credential hierarchy |
| Testability | ✅ Full | Singleton pattern for easy mocking, dependency injection, decoupled architecture |
| Documentation | ✅ Full | Comprehensive README with architecture, schema mapping, installation, security, and operational instructions |
Changelog
1.0.0
- Initial release
- Property Finder API integration with secure credential storage
- Full WP Residence schema mapping (40+ meta keys, 6 taxonomies)
- Bilingual support (Arabic/English) with Unicode detection
- Three-level agent mapping (PF Agent ID → Email → House Account)
- Action Scheduler with WP-Cron fallback
- Delta synchronization (timestamp + data hash)
- Content-based image deduplication (md5_file)
- Gallery reconciliation
- Admin log viewer with filtering, pagination, download, and clear
- Database index optimization
- Race condition protection with guaranteed lock release
- Comprehensive error handling with exponential backoff retry
- Security hardening (PHP exit guard, CSRF, sanitization, prepared SQL)