service_name = $service_name ?: static::class; // Allow plugins to extend the service initialization do_action('easy_invoice_service_initialized', $this); } /** * Get service name * * @since 1.0.0 * @return string */ public function getServiceName(): string { return $this->service_name; } /** * Log a message * * @since 1.0.0 * @param string $message The message to log * @param string $level The log level (debug, info, warning, error) * @return void */ protected function log(string $message, string $level = 'info'): void { $log_message = sprintf( '[%s] [%s] %s: %s', gmdate('Y-m-d H:i:s'), strtoupper($level), $this->service_name, $message ); // Allow plugins to handle logging do_action('easy_invoice_service_log', $log_message, $level, $this->service_name); } /** * Validate required parameters * * @since 1.0.0 * @param array $data The data to validate * @param array $required_fields The required field names * @return array Array of validation errors */ protected function validateRequiredFields(array $data, array $required_fields): array { $errors = []; foreach ($required_fields as $field) { if (!isset($data[$field]) || empty($data[$field])) { /* translators: %s: field name. */ $errors[] = sprintf(__('Field "%s" is required.', 'easy-invoice'), $field); } } // Allow plugins to add custom validation $errors = apply_filters('easy_invoice_service_validation_errors', $errors, $data, $required_fields, $this->service_name); return $errors; } /** * Sanitize data * * @since 1.0.0 * @param array $data The data to sanitize * @param array $sanitization_rules The sanitization rules * @return array The sanitized data */ protected function sanitizeData(array $data, array $sanitization_rules = []): array { $sanitized = []; foreach ($data as $key => $value) { $rule = $sanitization_rules[$key] ?? 'text_field'; switch ($rule) { case 'email': $sanitized[$key] = sanitize_email($value); break; case 'url': $sanitized[$key] = esc_url_raw($value); break; case 'int': $sanitized[$key] = intval($value); break; case 'float': $sanitized[$key] = floatval($value); break; case 'textarea': $sanitized[$key] = sanitize_textarea_field($value); break; case 'html': $sanitized[$key] = wp_kses_post($value); break; case 'array': $sanitized[$key] = is_array($value) ? array_map('sanitize_text_field', $value) : []; break; default: $sanitized[$key] = sanitize_text_field($value); break; } } // Allow plugins to modify sanitized data return apply_filters('easy_invoice_service_sanitized_data', $sanitized, $data, $sanitization_rules, $this->service_name); } /** * Format currency * * @since 1.0.0 * @param float $amount The amount to format * @param string $currency_code The currency code * @param string $position The currency position (left, right) * @return string The formatted currency */ protected function formatCurrency(float $amount, string $currency_code = 'USD', string $position = 'left'): string { $formatted = number_format($amount, 2); switch ($position) { case 'right': $formatted = $formatted . ' ' . $currency_code; break; case 'left': default: $formatted = $currency_code . ' ' . $formatted; break; } // Allow plugins to modify currency formatting return apply_filters('easy_invoice_service_currency_format', $formatted, $amount, $currency_code, $position, $this->service_name); } /** * Send email * * @since 1.0.0 * @param string $to The recipient email * @param string $subject The email subject * @param string $message The email message * @param array $headers The email headers * @return bool True if email was sent successfully */ protected function sendEmail(string $to, string $subject, string $message, array $headers = [], array $attachments = []): bool { // Allow plugins to modify email data // // $attachments defaults to empty, so every existing caller behaves exactly as // before. It exists so a document can be attached now that PdfRenderer can // produce one on the server — wp_mail() takes file paths, which is why the // renderer writes to a temp file rather than handing back bytes. $email_data = apply_filters('easy_invoice_service_email_data', [ 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers, 'attachments' => $attachments ], $this->service_name); // Allow plugins to handle email sending $sent = apply_filters('easy_invoice_service_email_send', null, $email_data, $this->service_name); if ($sent === null) { // Default WordPress email sending $sent = wp_mail( $email_data['to'], $email_data['subject'], $email_data['message'], $email_data['headers'], isset($email_data['attachments']) ? (array) $email_data['attachments'] : [] ); } // Log email sending $this->log(sprintf('Email sent to %s: %s', $to, $sent ? 'success' : 'failed'), $sent ? 'info' : 'error'); return $sent; } /** * Get plugin setting * * @since 1.0.0 * @param string $key The setting key * @param mixed $default The default value * @return mixed The setting value */ protected function getSetting(string $key, $default = null) { $value = get_option('easy_invoice_' . $key, $default); // Allow plugins to modify settings return apply_filters('easy_invoice_service_setting', $value, $key, $default, $this->service_name); } /** * Update plugin setting * * @since 1.0.0 * @param string $key The setting key * @param mixed $value The setting value * @return bool True if setting was updated successfully */ protected function updateSetting(string $key, $value): bool { // Allow plugins to modify setting before update $value = apply_filters('easy_invoice_service_setting_update', $value, $key, $this->service_name); $updated = update_option('easy_invoice_' . $key, $value); // Allow plugins to perform actions after setting update do_action('easy_invoice_service_setting_updated', $key, $value, $updated, $this->service_name); return $updated; } }