'https://inpost.pl/pl/pomoc/znajdz-przesylke?parcel=%s', 'dpd' => 'https://tracktrace.dpd.com.pl/parcelDetails?p1=%s', 'dhl' => 'https://www.dhl.com/pl-pl/home/sledzenie-przesylek.html?tracking-id=%s', 'pocztapolska' => 'https://emonitoring.poczta-polska.pl/?numer=%s', 'gls' => 'https://gls-group.com/PL/pl/sledzenie-paczek?match=%s', 'ups' => 'https://www.ups.com/track?loc=pl_PL&tracknum=%s', 'fedex' => 'https://www.fedex.com/fedextrack/?trknbr=%s', ]; private const CARRIER_HOSTS = [ 'inpost.pl' => 'inpost', 'dpd.com.pl' => 'dpd', 'dpd.pl' => 'dpd', 'dhl.com' => 'dhl', 'dhl.pl' => 'dhl', 'dhlparcel.pl' => 'dhl', 'poczta-polska.pl' => 'pocztapolska', 'gls-group.com' => 'gls', 'gls-group.eu' => 'gls', 'gls-pl.com' => 'gls', 'ups.com' => 'ups', 'fedex.com' => 'fedex', 'orlenpaczka.pl' => 'orlen', ]; private const CARRIER_KEYWORDS = [ 'inpost' => '/inpost|paczkomat/i', 'dpd' => '/\bdpd\b/i', 'dhl' => '/\bdhl\b/i', 'pocztapolska' => '/poczta\s+polska|pocztex|envelo/i', 'gls' => '/\bgls\b/i', 'ups' => '/\bups\b/i', 'fedex' => '/fedex/i', 'orlen' => '/orlen/i', ]; private const NUMBER_PARAMS = [ 'parcel', 'number', 'numer', 'p1', 'tracking-id', 'tracknum', 'match', 'trknbr', 'tracking_number' ]; /** * @return list */ public function resolve( WC_Order $order ): array { $fallbackCarrier = $this->carrierFromText( (string) $order->get_shipping_method() ); $tracking = $this->fromMeta( $order, $fallbackCarrier ); if ( \function_exists( 'wc_get_order_notes' ) ) { foreach ( (array) \wc_get_order_notes( [ 'order_id' => $order->get_id() ] ) as $note ) { if ( is_object( $note ) && isset( $note->content ) && is_string( $note->content ) ) { $tracking = array_merge( $tracking, $this->fromText( $note->content, $fallbackCarrier ) ); } } } $filtered = \apply_filters( 'agent001_order_tracking', $this->unique( $tracking ), $order ); return is_array( $filtered ) ? $this->unique( $filtered ) : []; } /** * @return list */ public function fromText( string $text, string $fallbackCarrier = '' ): array { $tracking = []; $text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); if ( preg_match_all( '~https://[^\s"\'<>]+~i', $text, $urls ) ) { foreach ( $urls[0] as $url ) { $entry = $this->fromUrl( rtrim( $url, '.,;)' ) ); if ( $entry !== null ) { $tracking[] = $entry; } } } $plain = \wp_strip_all_tags( $text ); if ( preg_match_all( '/przesy[łl]k\w*\D{0,40}?\b([A-Z0-9]{10,34})\b/iu', $plain, $numbers ) ) { $carrier = $this->carrierFromText( $plain ); $carrier = $carrier !== '' ? $carrier : $fallbackCarrier; foreach ( $numbers[1] as $number ) { if ( preg_match( '/\d/', $number ) ) { $tracking[] = $this->entry( $number, $carrier, '' ); } } } return $this->unique( $tracking ); } /** * @return list */ private function fromMeta( WC_Order $order, string $fallbackCarrier ): array { $tracking = []; foreach ( self::META_KEYS as $key ) { $value = $order->get_meta( $key, true ); if ( is_string( $value ) && $value !== '' ) { $tracking[] = $this->entry( $value, $fallbackCarrier, '' ); continue; } if ( ! is_array( $value ) ) { continue; } foreach ( $value as $item ) { if ( ! is_array( $item ) || empty( $item['tracking_number'] ) ) { continue; } $provider = (string) ( $item['tracking_provider'] ?? $item['custom_tracking_provider'] ?? '' ); $carrier = $this->carrierFromText( $provider ); $tracking[] = $this->entry( (string) $item['tracking_number'], $carrier !== '' ? $carrier : $fallbackCarrier, (string) ( $item['custom_tracking_link'] ?? '' ) ); } } return $tracking; } /** * @return array{number: string, carrier: string, url: string}|null */ private function fromUrl( string $url ): ?array { $host = strtolower( (string) \wp_parse_url( $url, PHP_URL_HOST ) ); $carrier = ''; foreach ( self::CARRIER_HOSTS as $domain => $slug ) { if ( $host === $domain || substr( $host, -strlen( '.' . $domain ) ) === '.' . $domain ) { $carrier = $slug; break; } } if ( $carrier === '' ) { return null; } parse_str( (string) \wp_parse_url( $url, PHP_URL_QUERY ), $query ); foreach ( self::NUMBER_PARAMS as $param ) { if ( isset( $query[ $param ] ) && is_string( $query[ $param ] ) && preg_match( '/\A[A-Za-z0-9]{6,40}\z/', $query[ $param ] ) ) { return $this->entry( $query[ $param ], $carrier, $url ); } } $segment = basename( (string) \wp_parse_url( $url, PHP_URL_PATH ) ); if ( preg_match( '/\A(?=.*\d)[A-Za-z0-9]{8,40}\z/', $segment ) ) { return $this->entry( $segment, $carrier, $url ); } return null; } /** * @return array{number: string, carrier: string, url: string} */ private function entry( string $number, string $carrier, string $url ): array { $number = trim( $number ); if ( $url === '' && isset( self::URL_TEMPLATES[ $carrier ] ) ) { $url = sprintf( self::URL_TEMPLATES[ $carrier ], rawurlencode( $number ) ); } return [ 'number' => $number, 'carrier' => $carrier, 'url' => strpos( $url, 'https://' ) === 0 ? \esc_url_raw( $url ) : '', ]; } private function carrierFromText( string $text ): string { foreach ( self::CARRIER_KEYWORDS as $carrier => $pattern ) { if ( preg_match( $pattern, $text ) ) { return $carrier; } } return ''; } /** * @param array $tracking * @return list */ private function unique( array $tracking ): array { $byNumber = []; foreach ( $tracking as $entry ) { if ( ! is_array( $entry ) || ! isset( $entry['number'] ) || ! is_string( $entry['number'] ) || trim( $entry['number'] ) === '' ) { continue; } $number = trim( $entry['number'] ); $current = $byNumber[ $number ] ?? null; $candidate = [ 'number' => $number, 'carrier' => is_string( $entry['carrier'] ?? null ) ? $entry['carrier'] : '', 'url' => is_string( $entry['url'] ?? null ) ? $entry['url'] : '', ]; if ( $current === null ) { $byNumber[ $number ] = $candidate; continue; } if ( $current['carrier'] === '' ) { $byNumber[ $number ]['carrier'] = $candidate['carrier']; } if ( $current['url'] === '' ) { $byNumber[ $number ]['url'] = $candidate['url']; } } return array_slice( array_values( $byNumber ), 0, 5 ); } }