Its path, or none when it could not be written. */ public static function ics_file( string $name, string $ics ): array { // wp_tempnam() lives in an admin include that REST and cron requests // do not load, and both send reservation mail. if ( ! function_exists( 'wp_tempnam' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $path = (string) wp_tempnam( $name . '.ics' ); if ( '' === $path ) { return []; } $ics_path = $path . '.ics'; if ( false === file_put_contents( $ics_path, $ics ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- writing a temp attachment. return []; } wp_delete_file( $path ); return [ $ics_path ]; } /** * @param list $attachments * @param string $reply_to Where a reply should go, '' for the sender. */ public function send( string $to, string $subject, string $body, array $attachments = [], string $reply_to = '' ): void { $headers = [ 'Content-Type: text/html; charset=UTF-8' ]; // The From address stays the site's own so mail still passes SPF; // the name is the business, not "WordPress". $admin_email = (string) get_option( 'admin_email', '' ); if ( '' !== $admin_email ) { $headers[] = sprintf( 'From: %s <%s>', self::from_name(), $admin_email ); } if ( '' !== $reply_to && strtolower( $reply_to ) !== strtolower( $to ) ) { $headers[] = 'Reply-To: ' . $reply_to; } wp_mail( $to, $subject, $body, $headers, $attachments ); foreach ( $attachments as $file ) { wp_delete_file( $file ); } } }