$query Any of q, status, field, page, per_page; empty ones are left out. * @return array{total: int, page: int, per_page: int, products: array, title: string, updated_at: string, preview_image_url: string}>}|null */ public static function fetch( array $query ): ?array { if ( ! Options::is_connected() ) { return null; } $request = self::request( $query ); $key = self::cache_key( $request ); $cached = self::parse( get_transient( $key ) ); if ( null !== $cached ) { return $cached; } $marker = isset( $request['field'] ) ? Options::ENRICHED_FIELD_FAILED_TRANSIENT : Options::ENRICHED_FAILED_TRANSIENT; if ( false !== get_transient( $marker ) ) { return null; } $timeout = isset( $request['field'] ) ? self::FIELD_TIMEOUT : self::TIMEOUT; $response = SignedClient::post( self::PATH, SignedClient::body( $request ), $timeout ); if ( ! $response->ok() ) { Log::warning( sprintf( 'Products list: CortexCommerce did not give the list (%s).', $response->describe() ) ); set_transient( $marker, 1, self::FAILURE_TTL ); return null; } $list = self::parse( $response->json ); if ( null === $list ) { Log::error( 'Products list: CortexCommerce answered the list request with an unexpected body.' ); set_transient( $marker, 1, self::FAILURE_TTL ); return null; } set_transient( $key, $list, self::TTL ); return $list; } /** * Throws away every cached list and the status panel's numbers, so the next look asks CortexCommerce again. */ public static function flush(): void { update_option( Options::ENRICHED_GENERATION, self::generation() + 1, false ); delete_transient( Options::ENRICHED_FAILED_TRANSIENT ); delete_transient( Options::ENRICHED_FIELD_FAILED_TRANSIENT ); delete_transient( Options::STATS_TRANSIENT ); } private static function generation(): int { return (int) get_option( Options::ENRICHED_GENERATION, 0 ); } /** * @param array $request Filters actually sent. */ private static function cache_key( array $request ): string { return Flavor::OPTION_PREFIX . 'enriched_' . self::generation() . '_' . md5( (string) wp_json_encode( $request ) ); } /** * @param array $query Raw filters. * @return array */ private static function request( array $query ): array { $request = array(); foreach ( array( 'q', 'status', 'field', 'page', 'per_page' ) as $key ) { if ( isset( $query[ $key ] ) && '' !== $query[ $key ] && 0 !== $query[ $key ] ) { $request[ $key ] = $query[ $key ]; } } return $request; } /** * @param mixed $raw Decoded body or cached value. * @return array{total: int, page: int, per_page: int, products: array, title: string, updated_at: string, preview_image_url: string}>}|null */ private static function parse( $raw ): ?array { if ( ! is_array( $raw ) || ! isset( $raw['products'] ) || ! is_array( $raw['products'] ) ) { return null; } foreach ( array( 'total', 'page', 'per_page' ) as $key ) { if ( ! isset( $raw[ $key ] ) || ! is_int( $raw[ $key ] ) || $raw[ $key ] < 0 ) { return null; } } $products = array(); foreach ( $raw['products'] as $row ) { $product = self::parse_row( $row ); if ( null !== $product ) { $products[] = $product; } } $dropped = count( $raw['products'] ) - count( $products ); if ( $dropped > 0 ) { Log::warning( sprintf( 'Products list: %d rows from CortexCommerce were left out because they were malformed.', $dropped ) ); } return array( 'total' => $raw['total'], 'page' => $raw['page'], 'per_page' => $raw['per_page'], 'products' => $products, ); } /** * @param mixed $row One row of the body. * @return array{product_id: string, status: string, fields: array, title: string, updated_at: string, preview_image_url: string}|null */ private static function parse_row( $row ): ?array { if ( ! is_array( $row ) || ! isset( $row['product_id'], $row['status'] ) || ! is_string( $row['product_id'] ) || '' === $row['product_id'] || ! in_array( $row['status'], self::STATUSES, true ) ) { return null; } $fields = array(); if ( isset( $row['fields'] ) && is_array( $row['fields'] ) ) { foreach ( $row['fields'] as $field ) { if ( is_string( $field ) && in_array( $field, self::FIELDS, true ) && ! in_array( $field, $fields, true ) ) { $fields[] = $field; } } } $preview = isset( $row['preview_image_url'] ) && is_string( $row['preview_image_url'] ) ? $row['preview_image_url'] : ''; return array( 'product_id' => $row['product_id'], 'status' => $row['status'], 'fields' => $fields, 'title' => isset( $row['title'] ) && is_string( $row['title'] ) ? $row['title'] : '', 'updated_at' => isset( $row['updated_at'] ) && is_string( $row['updated_at'] ) ? $row['updated_at'] : '', 'preview_image_url' => 0 === strpos( $preview, 'https://' ) ? $preview : '', ); } }