111 lines
5.2 KiB
Markdown
111 lines
5.2 KiB
Markdown
# Coding Conventions
|
|
|
|
**Analysis Date:** 2026-05-05
|
|
|
|
## PHP Style
|
|
|
|
**Standard:** WordPress Coding Standards (not PSR-12).
|
|
|
|
- 1 tab indentation
|
|
- `array()` long-form always — never `[]` short syntax in plugin code
|
|
- Spaces inside control structure parens: `if ( $condition )`, `foreach ( $items as $item )`
|
|
- No spaces inside function call parens: `get_post_meta( $id, '_key', true )`
|
|
- Every PHP file starts with direct-access guard:
|
|
```php
|
|
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
|
```
|
|
- File-level docblock with `@package YachtBooking`
|
|
|
|
## Naming
|
|
|
|
| Thing | Convention | Example |
|
|
|-------|-----------|---------|
|
|
| Classes | PascalCase (underscore for multi-word) | `Rest_Controller`, `Yacht_Booking` |
|
|
| Methods | snake_case | `get_yacht_id()`, `mark_as_booked()` |
|
|
| Variables | snake_case | `$booking_id`, `$start_date` |
|
|
| Constants | SCREAMING_SNAKE_CASE | `YACHT_BOOKING_VERSION` |
|
|
| Post meta keys | underscore-prefixed, namespaced | `_booking_start_date`, `_yacht_capacity` |
|
|
| WP option keys | `yacht_booking_` prefix | `yacht_booking_version` |
|
|
| Boolean getters | `is_` / `has_` prefix | `is_available()`, `has_yacht_calendar_widget()` |
|
|
| Static getters | `get_{thing}($id)` | `Booking::get_customer_name($id)` |
|
|
| Static updaters | `update_{thing}($id, $value)` | `Yacht::update_capacity($id, $v)` |
|
|
|
|
## Class Patterns
|
|
|
|
**Singleton** — for hook-registering classes (must only run once):
|
|
```php
|
|
private static $instance = null;
|
|
public static function get_instance() {
|
|
if ( null === self::$instance ) { self::$instance = new self(); }
|
|
return self::$instance;
|
|
}
|
|
private function __construct() { /* register hooks here */ }
|
|
```
|
|
Used by: `Yacht_Booking`, `Admin`, `Shortcode`, `Sync_Controller`
|
|
|
|
**Static utility** — for pure data accessors with no instance state:
|
|
```php
|
|
public static function get_customer_name( $booking_id ) {
|
|
return get_post_meta( $booking_id, '_booking_customer_name', true );
|
|
}
|
|
```
|
|
Used by: `Booking`, `Yacht`, `Availability`, `Settings`, `Email_Templates`
|
|
|
|
**All WP hook registration goes in constructor or `init_hooks()` method.**
|
|
|
|
## Security Patterns
|
|
|
|
- **Nonces in admin forms:** `wp_nonce_field()` on output, `check_admin_referer()` on submit
|
|
- **Nonces in REST endpoints:** `X-WP-Nonce` header verified with `wp_verify_nonce($header, 'wp_rest')`
|
|
- **iCal feed:** per-yacht token in `_yacht_ical_token` meta, compared with `hash_equals()`
|
|
- **Admin REST endpoints:** `current_user_can('yacht_booking_manage_bookings')`
|
|
- **Sanitization on input:** `sanitize_text_field()`, `sanitize_email()`, `absint()`, `wp_kses_post()`
|
|
- **Escaping on output:** `esc_html()`, `esc_attr()`, `esc_url()`, `wp_kses_post()`
|
|
- **All `$wpdb` queries:** use `$wpdb->prepare()` — no raw SQL
|
|
|
|
## WordPress Integration Patterns
|
|
|
|
- CPT registration via static `register()` method called by orchestrator
|
|
- Options: `add_option()` (idempotent) on activation, `get_option()` / `update_option()` for runtime
|
|
- Capability checks use custom caps (`yacht_booking_manage_bookings`), not `manage_options`
|
|
- Admin form submissions: POST → `admin_init` handler → `wp_safe_redirect()` (PRG pattern)
|
|
- Error feedback: `?error=message` query string + `admin_notices` hook display
|
|
|
|
## JavaScript Style
|
|
|
|
- **Pattern:** IIFE `(function($) { 'use strict'; ... })(jQuery)` in both JS files
|
|
- **Variable naming:** camelCase; jQuery-wrapped objects prefixed with `$` (`$form`, `$submitBtn`)
|
|
- **`calendar.js`:** Named inner functions (`initYachtCalendar`, `formatDate`, `escapeHtml`); uses `const`/`let`
|
|
- **`admin.js`:** Object-literal module pattern (`const YachtBookingAdmin = { init, bindEvents, handleManualSync }`)
|
|
- **Data bridge:** `wp_localize_script()` injects `yachtBookingData` (apiUrl, nonce, bookingEnabled, i18n) and `yachtBookingAdmin`
|
|
- **Error display:** Inline HTML into `.yacht-booking-response` / `.yacht-inquiry-response` divs; messages from `yachtBookingData.i18n`
|
|
|
|
## CSS / Markup
|
|
|
|
- BEM-like kebab-case with `yacht-` prefix: `.yacht-calendar-wrapper`, `.yacht-booking-form-container`
|
|
- Modifier suffixes: `.yacht-calendar-view-only`, `.yacht-legend-swatch-past`
|
|
- JS state classes: `.is-active`, `.yacht-day-available`, `.yacht-day-booked`
|
|
- Mobile-first approach
|
|
|
|
## Internationalisation
|
|
|
|
- All user-visible strings: `__()`, `esc_html__()`, `esc_attr_e()` with domain `'yacht-booking'`
|
|
- Strings with variables: `printf( esc_html__( 'Text %s', 'yacht-booking' ), esc_html( $var ) )` — never concatenation
|
|
- Translators hint: `/* translators: %s: yacht name */` before `printf`
|
|
|
|
## Documentation
|
|
|
|
- Public/protected methods always get docblocks; private helpers sometimes omitted
|
|
- Inline block comments label logical sections: `// Get yacht title`, `// Save booking meta`
|
|
- HTML comments label template sections in Polish: `<!-- Nazwa jachtu -->`
|
|
- JS functions get JSDoc blocks: `/** Format date to YYYY-MM-DD */`
|
|
- PHPDoc generics notation for complex types: `@return array<int, array<string, mixed>>`
|
|
|
|
## Error Handling
|
|
|
|
- PHP: return `false` or `WP_Error` on failure
|
|
- REST: return `new \WP_Error( 'code', message, array( 'status' => 4xx ) )`
|
|
- No exceptions used anywhere in the plugin
|
|
- GCal API: `is_wp_error()` check only — HTTP error codes are NOT checked (known gap)
|
|
- Logging: `error_log()` with prefixes only when `WP_DEBUG === true`; nothing in production
|