Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Helper to massage Podcast data to be used in the Podcast block. |
||
| 4 | * |
||
| 5 | * @package jetpack |
||
| 6 | */ |
||
| 7 | |||
| 8 | /** |
||
| 9 | * Class Jetpack_Podcast_Helper |
||
| 10 | */ |
||
| 11 | class Jetpack_Podcast_Helper { |
||
| 12 | /** |
||
| 13 | * Gets podcast data formatted to be used by the Podcast Player block in both server-side |
||
| 14 | * block rendering and in API `WPCOM_REST_API_V2_Endpoint_Podcast_Player`. |
||
| 15 | * |
||
| 16 | * The result is cached for one hour. |
||
| 17 | * |
||
| 18 | * @param string $feed The RSS feed to load and list tracks for. |
||
| 19 | * @return array|WP_Error The player data or a error object. |
||
| 20 | */ |
||
| 21 | public static function get_player_data( $feed ) { |
||
| 22 | $feed = esc_url_raw( $feed ); |
||
| 23 | |||
| 24 | // Try loading data from the cache. |
||
| 25 | $transient_key = 'jetpack_podcast_' . md5( $feed ); |
||
| 26 | $player_data = get_transient( $transient_key ); |
||
| 27 | |||
| 28 | // Fetch data if we don't have any cached. |
||
| 29 | if ( false === $player_data ) { |
||
| 30 | // Load feed. |
||
| 31 | $rss = self::load_feed( $feed ); |
||
| 32 | if ( is_wp_error( $rss ) ) { |
||
| 33 | return $rss; |
||
| 34 | } |
||
| 35 | |||
| 36 | // Get tracks. |
||
| 37 | $tracks = self::get_track_list( $rss ); |
||
|
0 ignored issues
–
show
|
|||
| 38 | |||
| 39 | // Get podcast meta. |
||
| 40 | $title = $rss->get_title(); |
||
|
0 ignored issues
–
show
The method
get_title() does not seem to exist on object<WP_Error>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 41 | $title = self::get_plain_text( $title ); |
||
| 42 | |||
| 43 | $cover = $rss->get_image_url(); |
||
|
0 ignored issues
–
show
The method
get_image_url() does not seem to exist on object<WP_Error>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 44 | $cover = ! empty( $cover ) ? esc_url( $cover ) : null; |
||
| 45 | |||
| 46 | $link = $rss->get_link(); |
||
|
0 ignored issues
–
show
The method
get_link() does not seem to exist on object<WP_Error>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 47 | $link = ! empty( $link ) ? esc_url( $link ) : null; |
||
| 48 | |||
| 49 | $player_data = array( |
||
| 50 | 'title' => $title, |
||
| 51 | 'link' => $link, |
||
| 52 | 'cover' => $cover, |
||
| 53 | 'tracks' => $tracks, |
||
| 54 | ); |
||
| 55 | |||
| 56 | // Cache for 1 hour. |
||
| 57 | set_transient( $transient_key, $player_data, HOUR_IN_SECONDS ); |
||
| 58 | } |
||
| 59 | |||
| 60 | return $player_data; |
||
| 61 | } |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Gets a list of tracks for the supplied RSS feed. |
||
| 65 | * |
||
| 66 | * @param string $rss The RSS feed to load and list tracks for. |
||
| 67 | * @return array|WP_Error The feed's tracks or a error object. |
||
| 68 | */ |
||
| 69 | private static function get_track_list( $rss ) { |
||
| 70 | // Get first ten items and format them. |
||
| 71 | $track_list = array_map( array( __CLASS__, 'setup_tracks_callback' ), $rss->get_items( 0, 10 ) ); |
||
|
0 ignored issues
–
show
|
|||
| 72 | |||
| 73 | // Remove empty tracks. |
||
| 74 | return array_filter( $track_list ); |
||
| 75 | } |
||
| 76 | |||
| 77 | /** |
||
| 78 | * Formats string as pure plaintext, with no HTML tags or entities present. |
||
| 79 | * This is ready to be used in React, innerText but needs to be escaped |
||
| 80 | * using standard `esc_html` when generating markup on server. |
||
| 81 | * |
||
| 82 | * @param string $str Input string. |
||
| 83 | * @return string Plain text string. |
||
| 84 | */ |
||
| 85 | private static function get_plain_text( $str ) { |
||
| 86 | // Trim string and return if empty. |
||
| 87 | $str = trim( (string) $str ); |
||
| 88 | if ( empty( $str ) ) { |
||
| 89 | return ''; |
||
| 90 | } |
||
| 91 | |||
| 92 | // Replace all entities with their characters, including all types of quotes. |
||
| 93 | $str = wp_specialchars_decode( $str, ENT_QUOTES ); |
||
| 94 | |||
| 95 | // Make sure there are no tags. |
||
| 96 | $str = wp_strip_all_tags( $str ); |
||
| 97 | |||
| 98 | return $str; |
||
| 99 | } |
||
| 100 | |||
| 101 | /** |
||
| 102 | * Loads an RSS feed using `fetch_feed`. |
||
| 103 | * |
||
| 104 | * @param string $feed The RSS feed URL to load. |
||
| 105 | * @return SimplePie|WP_Error The RSS object or error. |
||
| 106 | */ |
||
| 107 | View Code Duplication | private static function load_feed( $feed ) { |
|
| 108 | $rss = fetch_feed( esc_url_raw( $feed ) ); |
||
| 109 | |||
| 110 | if ( is_wp_error( $rss ) ) { |
||
| 111 | return new WP_Error( 'invalid_url', __( 'Your podcast couldn\'t be embedded. Please double check your URL.', 'jetpack' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_url'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 112 | } |
||
| 113 | |||
| 114 | if ( ! $rss->get_item_quantity() ) { |
||
| 115 | return new WP_Error( 'no_tracks', __( 'Podcast audio RSS feed has no tracks.', 'jetpack' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'no_tracks'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 116 | } |
||
| 117 | |||
| 118 | return $rss; |
||
| 119 | } |
||
| 120 | |||
| 121 | /** |
||
| 122 | * Prepares Episode data to be used by the Podcast Player block. |
||
| 123 | * |
||
| 124 | * @param SimplePie_Item $episode SimplePie_Item object, representing a podcast episode. |
||
| 125 | * @return array |
||
| 126 | */ |
||
| 127 | private static function setup_tracks_callback( SimplePie_Item $episode ) { |
||
| 128 | $enclosure = self::get_audio_enclosure( $episode ); |
||
| 129 | |||
| 130 | // If there is no link return an empty array. We will filter out later. |
||
| 131 | if ( empty( $enclosure->link ) ) { |
||
| 132 | return array(); |
||
| 133 | } |
||
| 134 | |||
| 135 | // Build track data. |
||
| 136 | $track = array( |
||
| 137 | 'id' => wp_unique_id( 'podcast-track-' ), |
||
| 138 | 'link' => esc_url( $episode->get_link() ), |
||
| 139 | 'src' => esc_url( $enclosure->link ), |
||
| 140 | 'type' => esc_attr( $enclosure->type ), |
||
| 141 | 'description' => self::get_plain_text( $episode->get_description() ), |
||
| 142 | 'title' => self::get_plain_text( $episode->get_title() ), |
||
| 143 | ); |
||
| 144 | |||
| 145 | if ( empty( $track['title'] ) ) { |
||
| 146 | $track['title'] = esc_html__( '(no title)', 'jetpack' ); |
||
| 147 | } |
||
| 148 | |||
| 149 | if ( ! empty( $enclosure->duration ) ) { |
||
| 150 | $track['duration'] = esc_html( self::format_track_duration( $enclosure->duration ) ); |
||
| 151 | } |
||
| 152 | |||
| 153 | return $track; |
||
| 154 | } |
||
| 155 | |||
| 156 | /** |
||
| 157 | * Retrieves an audio enclosure. |
||
| 158 | * |
||
| 159 | * @param SimplePie_Item $episode SimplePie_Item object, representing a podcast episode. |
||
| 160 | * @return SimplePie_Enclosure|null |
||
| 161 | */ |
||
| 162 | private static function get_audio_enclosure( SimplePie_Item $episode ) { |
||
| 163 | foreach ( (array) $episode->get_enclosures() as $enclosure ) { |
||
| 164 | if ( 0 === strpos( $enclosure->type, 'audio/' ) ) { |
||
| 165 | return $enclosure; |
||
| 166 | } |
||
| 167 | } |
||
| 168 | |||
| 169 | // Default to empty SimplePie_Enclosure object. |
||
| 170 | return $episode->get_enclosure(); |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Returns the track duration as a formatted string. |
||
| 175 | * |
||
| 176 | * @param number $duration of the track in seconds. |
||
| 177 | * @return string |
||
| 178 | */ |
||
| 179 | private static function format_track_duration( $duration ) { |
||
| 180 | $format = $duration > HOUR_IN_SECONDS ? 'H:i:s' : 'i:s'; |
||
| 181 | |||
| 182 | return date_i18n( $format, $duration ); |
||
| 183 | } |
||
| 184 | } |
||
| 185 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.