├── .gitignore ├── inc ├── class-my-photon-settings.php ├── class-my-photon.php └── functions.php ├── my-photon.php └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /inc/class-my-photon-settings.php: -------------------------------------------------------------------------------- 1 | false, 11 | 'base-url' => '', 12 | ); 13 | 14 | const SLUG = 'my-photon'; 15 | 16 | protected static $instance; 17 | 18 | public static function instance() { 19 | if ( ! isset( self::$instance ) ) { 20 | self::$instance = new My_Photon_Settings; 21 | self::$instance->setup_actions(); 22 | } 23 | return self::$instance; 24 | } 25 | 26 | protected function __construct() { 27 | /** Don't do anything **/ 28 | } 29 | 30 | public static function get( $key = null, $default = null ) { 31 | if ( ! isset( self::$options ) ) { 32 | self::$options = get_option( self::SLUG, array() ); 33 | self::$options = wp_parse_args( self::$options, self::$defaults ); 34 | } 35 | 36 | if ( isset( $key ) ) { 37 | return ! empty( self::$options[ $key ] ) ? self::$options[ $key ] : $default; 38 | } else { 39 | return self::$options; 40 | } 41 | } 42 | 43 | protected function setup_actions() { 44 | add_action( 'admin_init', array( self::$instance, 'action_admin_init' ) ); 45 | add_action( 'admin_menu', array( self::$instance, 'action_admin_menu' ) ); 46 | } 47 | 48 | public function action_admin_init() { 49 | register_setting( self::SLUG, self::SLUG, array( self::$instance, 'sanitize_options' ) ); 50 | add_settings_section( 'general', false, '__return_false', self::SLUG ); 51 | add_settings_field( 'active', __( 'Activate My Photon', 'my-photon' ), array( self::$instance, 'field' ), self::SLUG, 'general', array( 'name' => 'active', 'type' => 'checkbox', 'label' => __( 'Active', 'my-photon' ) ) ); 52 | add_settings_field( 'base-url', __( 'Base URL', 'my-photon' ), array( self::$instance, 'field' ), self::SLUG, 'general', array( 'name' => 'base-url' ) ); 53 | } 54 | 55 | public function action_admin_menu() { 56 | add_options_page( __( 'My Photon Settings', 'my-photon' ), __( 'My Photon Settings', 'my-photon' ), $this->options_capability, self::SLUG, array( self::$instance, 'view_settings_page' ) ); 57 | } 58 | 59 | public function field( $args ) { 60 | $args = wp_parse_args( $args, array( 61 | 'name' => '', 62 | 'type' => 'text', 63 | 'label' => null, 64 | ) ); 65 | switch ( $args['type'] ) { 66 | case 'checkbox' : 67 | printf( 68 | '', 69 | self::SLUG, 70 | esc_attr( $args['name'] ), 71 | checked( $this->get( $args['name'] ), true, false ), 72 | $args['label'] 73 | ); 74 | break; 75 | 76 | default : 77 | printf( '', self::SLUG, esc_attr( $args['name'] ), esc_attr( $this->get( $args['name'] ) ) ); 78 | break; 79 | } 80 | } 81 | 82 | public function sanitize_options( $in ) { 83 | $in = wp_parse_args( $in, self::$defaults ); 84 | 85 | // Validate base-url 86 | $out['active'] = ( '1' == $in['active'] ); 87 | $out['base-url'] = esc_url_raw( $in['base-url'] ); 88 | return $out; 89 | } 90 | 91 | public function view_settings_page() { 92 | ?>
93 |

94 |
95 | 96 | 97 | 98 |
99 |
100 | setup(); 33 | } 34 | 35 | return self::$__instance; 36 | } 37 | 38 | /** 39 | * Silence is golden. 40 | */ 41 | private function __construct() {} 42 | 43 | /** 44 | * Register actions and filters, but only if basic Photon functions are available. 45 | * The basic functions are found in ./functions.photon.php. 46 | * 47 | * @uses add_action, add_filter 48 | * @return null 49 | */ 50 | private function setup() { 51 | if ( ! function_exists( 'my_photon_url' ) ) 52 | return; 53 | 54 | // Images in post content and galleries 55 | add_filter( 'the_content', array( __CLASS__, 'filter_the_content' ), 999999 ); 56 | add_filter( 'get_post_galleries', array( __CLASS__, 'filter_the_galleries' ), 999999 ); 57 | 58 | // Core image retrieval 59 | add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 10, 3 ); 60 | } 61 | 62 | 63 | /** 64 | ** IN-CONTENT IMAGE MANIPULATION FUNCTIONS 65 | **/ 66 | 67 | /** 68 | * Match all images and any relevant tags in a block of HTML. 69 | * 70 | * @param string $content Some HTML. 71 | * @return array An array of $images matches, where $images[0] is 72 | * an array of full matches, and the link_url, img_tag, 73 | * and img_url keys are arrays of those matches. 74 | */ 75 | public static function parse_images_from_html( $content ) { 76 | $images = array(); 77 | 78 | if ( preg_match_all( '#(?:]+?href=["|\'](?P[^\s]+?)["|\'][^>]*?>\s*)?(?P]+?src=["|\'](?P[^\s]+?)["|\'].*?>){1}(?:\s*)?#is', $content, $images ) ) { 79 | foreach ( $images as $key => $unused ) { 80 | // Simplify the output as much as possible, mostly for confirming test results. 81 | if ( is_numeric( $key ) && $key > 0 ) 82 | unset( $images[$key] ); 83 | } 84 | 85 | return $images; 86 | } 87 | 88 | return array(); 89 | } 90 | 91 | /** 92 | * Try to determine height and width from strings WP appends to resized image filenames. 93 | * 94 | * @param string $src The image URL. 95 | * @return array An array consisting of width and height. 96 | */ 97 | public static function parse_dimensions_from_filename( $src ) { 98 | $width_height_string = array(); 99 | 100 | if ( preg_match( '#-(\d+)x(\d+)\.(?:' . implode('|', self::$extensions ) . '){1}$#i', $src, $width_height_string ) ) { 101 | $width = (int) $width_height_string[1]; 102 | $height = (int) $width_height_string[2]; 103 | 104 | if ( $width && $height ) 105 | return array( $width, $height ); 106 | } 107 | 108 | return array( false, false ); 109 | } 110 | 111 | /** 112 | * Identify images in post content, and if images are local (uploaded to the current site), pass through Photon. 113 | * 114 | * @param string $content 115 | * @uses self::validate_image_url, apply_filters, my_photon_url, esc_url 116 | * @filter the_content 117 | * @return string 118 | */ 119 | public static function filter_the_content( $content ) { 120 | 121 | $images = My_Photon::parse_images_from_html( $content ); 122 | 123 | if ( ! empty( $images ) ) { 124 | global $content_width; 125 | 126 | $image_sizes = self::image_sizes(); 127 | $upload_dir = wp_upload_dir(); 128 | 129 | foreach ( $images[0] as $index => $tag ) { 130 | // Default to resize, though fit may be used in certain cases where a dimension cannot be ascertained 131 | $transform = 'resize'; 132 | 133 | // Start with a clean attachment ID each time 134 | $attachment_id = false; 135 | 136 | // Flag if we need to munge a fullsize URL 137 | $fullsize_url = false; 138 | 139 | // Identify image source 140 | $src = $src_orig = $images['img_url'][ $index ]; 141 | 142 | // Allow specific images to be skipped 143 | if ( apply_filters( 'my_photon_skip_image', false, $src, $tag ) ) 144 | continue; 145 | 146 | // Support Automattic's Lazy Load plugin 147 | // Can't modify $tag yet as we need unadulterated version later 148 | if ( preg_match( '#data-lazy-src=["|\'](.+?)["|\']#i', $images['img_tag'][ $index ], $lazy_load_src ) ) { 149 | $placeholder_src = $placeholder_src_orig = $src; 150 | $src = $src_orig = $lazy_load_src[1]; 151 | } 152 | 153 | // Check if image URL should be used with Photon 154 | if ( self::validate_image_url( $src ) ) { 155 | // Find the width and height attributes 156 | $width = $height = false; 157 | 158 | // First, check the image tag 159 | if ( preg_match( '#width=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $width_string ) ) 160 | $width = $width_string[1]; 161 | 162 | if ( preg_match( '#height=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $height_string ) ) 163 | $height = $height_string[1]; 164 | 165 | // Can't pass both a relative width and height, so unset the height in favor of not breaking the horizontal layout. 166 | if ( false !== strpos( $width, '%' ) && false !== strpos( $height, '%' ) ) 167 | $width = $height = false; 168 | 169 | // Detect WP registered image size from HTML class 170 | if ( preg_match( '#class=["|\']?[^"\']*size-([^"\'\s]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $size ) ) { 171 | $size = array_pop( $size ); 172 | 173 | if ( false === $width && false === $height && 'full' != $size && array_key_exists( $size, $image_sizes ) ) { 174 | $width = (int) $image_sizes[ $size ]['width']; 175 | $height = (int) $image_sizes[ $size ]['height']; 176 | $transform = $image_sizes[ $size ]['crop'] ? 'resize' : 'fit'; 177 | } 178 | } else { 179 | unset( $size ); 180 | } 181 | 182 | // WP Attachment ID, if uploaded to this site 183 | if ( preg_match( '#class=["|\']?[^"\']*wp-image-([\d]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $attachment_id ) && ( 0 === strpos( $src, $upload_dir['baseurl'] ) || apply_filters( 'my_photon_image_is_local', false, compact( 'src', 'tag', 'images', 'index' ) ) ) ) { 184 | $attachment_id = intval( array_pop( $attachment_id ) ); 185 | 186 | if ( $attachment_id ) { 187 | $attachment = get_post( $attachment_id ); 188 | 189 | // Basic check on returned post object 190 | if ( is_object( $attachment ) && ! is_wp_error( $attachment ) && 'attachment' == $attachment->post_type ) { 191 | $src_per_wp = wp_get_attachment_image_src( $attachment_id, isset( $size ) ? $size : 'full' ); 192 | 193 | if ( self::validate_image_url( $src_per_wp[0] ) ) { 194 | $src = $src_per_wp[0]; 195 | $fullsize_url = true; 196 | 197 | // Prevent image distortion if a detected dimension exceeds the image's natural dimensions 198 | if ( ( false !== $width && $width > $src_per_wp[1] ) || ( false !== $height && $height > $src_per_wp[2] ) ) { 199 | $width = false == $width ? false : min( $width, $src_per_wp[1] ); 200 | $height = false == $height ? false : min( $height, $src_per_wp[2] ); 201 | } 202 | 203 | // If no width and height are found, max out at source image's natural dimensions 204 | // Otherwise, respect registered image sizes' cropping setting 205 | if ( false == $width && false == $height ) { 206 | $width = $src_per_wp[1]; 207 | $height = $src_per_wp[2]; 208 | $transform = 'fit'; 209 | } elseif ( isset( $size ) && array_key_exists( $size, $image_sizes ) && isset( $image_sizes[ $size ]['crop'] ) ) { 210 | $transform = (bool) $image_sizes[ $size ]['crop'] ? 'resize' : 'fit'; 211 | } 212 | } 213 | } else { 214 | unset( $attachment_id ); 215 | unset( $attachment ); 216 | } 217 | } 218 | } 219 | 220 | // If image tag lacks width and height arguments, try to determine from strings WP appends to resized image filenames. 221 | if ( false === $width && false === $height ) { 222 | list( $width, $height ) = My_Photon::parse_dimensions_from_filename( $src ); 223 | } 224 | 225 | // If width is available, constrain to $content_width 226 | if ( false !== $width && false === strpos( $width, '%' ) && is_numeric( $content_width ) ) { 227 | if ( $width > $content_width && false !== $height && false === strpos( $height, '%' ) ) { 228 | $height = round( ( $content_width * $height ) / $width ); 229 | $width = $content_width; 230 | } elseif ( $width > $content_width ) { 231 | $width = $content_width; 232 | } 233 | } 234 | 235 | // Set a width if none is found and $content_width is available 236 | // If width is set in this manner and height is available, use `fit` instead of `resize` to prevent skewing 237 | if ( false === $width && is_numeric( $content_width ) ) { 238 | $width = (int) $content_width; 239 | 240 | if ( false !== $height ) 241 | $transform = 'fit'; 242 | } 243 | 244 | // Detect if image source is for a custom-cropped thumbnail and prevent further URL manipulation. 245 | if ( ! $fullsize_url && preg_match_all( '#-e[a-z0-9]+(-\d+x\d+)?\.(' . implode('|', self::$extensions ) . '){1}$#i', basename( $src ), $filename ) ) 246 | $fullsize_url = true; 247 | 248 | // Build URL, first maybe removing WP's resized string so we pass the original image to Photon 249 | if ( ! $fullsize_url ) { 250 | $src = self::strip_image_dimensions_maybe( $src ); 251 | } 252 | 253 | // Build array of Photon args and expose to filter before passing to Photon URL function 254 | $args = array(); 255 | 256 | if ( false !== $width && false !== $height && false === strpos( $width, '%' ) && false === strpos( $height, '%' ) ) 257 | $args[ $transform ] = $width . ',' . $height; 258 | elseif ( false !== $width ) 259 | $args['w'] = $width; 260 | elseif ( false !== $height ) 261 | $args['h'] = $height; 262 | 263 | $args = apply_filters( 'my_photon_post_image_args', $args, compact( 'tag', 'src', 'src_orig', 'width', 'height' ) ); 264 | 265 | $photon_url = my_photon_url( $src, $args ); 266 | 267 | // Modify image tag if Photon function provides a URL 268 | // Ensure changes are only applied to the current image by copying and modifying the matched tag, then replacing the entire tag with our modified version. 269 | if ( $src != $photon_url ) { 270 | $new_tag = $tag; 271 | 272 | // If present, replace the link href with a Photoned URL for the full-size image. 273 | if ( ! empty( $images['link_url'][ $index ] ) && self::validate_image_url( $images['link_url'][ $index ] ) ) 274 | $new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . my_photon_url( $images['link_url'][ $index ] ) . '\2', $new_tag, 1 ); 275 | 276 | // Supplant the original source value with our Photon URL 277 | $photon_url = esc_url( $photon_url ); 278 | $new_tag = str_replace( $src_orig, $photon_url, $new_tag ); 279 | 280 | // If Lazy Load is in use, pass placeholder image through Photon 281 | if ( isset( $placeholder_src ) && self::validate_image_url( $placeholder_src ) ) { 282 | $placeholder_src = my_photon_url( $placeholder_src ); 283 | 284 | if ( $placeholder_src != $placeholder_src_orig ) 285 | $new_tag = str_replace( $placeholder_src_orig, esc_url( $placeholder_src ), $new_tag ); 286 | 287 | unset( $placeholder_src ); 288 | } 289 | 290 | // Remove the width and height arguments from the tag to prevent distortion 291 | $new_tag = preg_replace( '#(?<=\s)(width|height)=["|\']?[\d%]+["|\']?\s?#i', '', $new_tag ); 292 | 293 | // Tag an image for dimension checking 294 | $new_tag = preg_replace( '#(\s?/)?>()?$#i', ' data-recalc-dims="1"\1>\2', $new_tag ); 295 | 296 | // Replace original tag with modified version 297 | $content = str_replace( $tag, $new_tag, $content ); 298 | } 299 | } elseif ( false !== strpos( $src, My_Photon_Settings::get( 'base-url' ) ) && ! empty( $images['link_url'][ $index ] ) && self::validate_image_url( $images['link_url'][ $index ] ) ) { 300 | $new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . my_photon_url( $images['link_url'][ $index ] ) . '\2', $tag, 1 ); 301 | 302 | $content = str_replace( $tag, $new_tag, $content ); 303 | } 304 | } 305 | } 306 | 307 | return $content; 308 | } 309 | 310 | /** 311 | * Filter Core galleries 312 | * 313 | * @param array $galleries Gallery array. 314 | * @return array 315 | */ 316 | public static function filter_the_galleries( $galleries ) { 317 | if ( empty( $galleries ) || ! is_array( $galleries ) ) { 318 | return $galleries; 319 | } 320 | 321 | // Pass by reference, so we can modify them in place. 322 | foreach ( $galleries as &$this_gallery ) { 323 | if ( is_string( $this_gallery ) ) { 324 | $this_gallery = self::filter_the_content( $this_gallery ); 325 | } 326 | } 327 | unset( $this_gallery ); // break the reference. 328 | 329 | return $galleries; 330 | } 331 | 332 | /** 333 | ** CORE IMAGE RETRIEVAL 334 | **/ 335 | 336 | /** 337 | * Filter post thumbnail image retrieval, passing images through Photon 338 | * 339 | * @param string|bool $image 340 | * @param int $attachment_id 341 | * @param string|array $size 342 | * @uses is_admin, apply_filters, wp_get_attachment_url, self::validate_image_url, this::image_sizes, my_photon_url 343 | * @filter image_downsize 344 | * @return string|bool 345 | */ 346 | public function filter_image_downsize( $image, $attachment_id, $size ) { 347 | // Don't foul up the admin side of things, and provide plugins a way of preventing Photon from being applied to images. 348 | if ( is_admin() || apply_filters( 'my_photon_override_image_downsize', false, compact( 'image', 'attachment_id', 'size' ) ) ) 349 | return $image; 350 | 351 | // Get the image URL and proceed with Photon-ification if successful 352 | $image_url = wp_get_attachment_url( $attachment_id ); 353 | 354 | if ( $image_url ) { 355 | // Check if image URL should be used with Photon 356 | if ( ! self::validate_image_url( $image_url ) ) 357 | return $image; 358 | 359 | // If an image is requested with a size known to WordPress, use that size's settings with Photon 360 | if ( ( is_string( $size ) || is_int( $size ) ) && array_key_exists( $size, self::image_sizes() ) ) { 361 | $image_args = self::image_sizes(); 362 | $image_args = $image_args[ $size ]; 363 | 364 | $photon_args = array(); 365 | 366 | // `full` is a special case in WP 367 | // To ensure filter receives consistent data regardless of requested size, `$image_args` is overridden with dimensions of original image. 368 | if ( 'full' == $size ) { 369 | $image_meta = wp_get_attachment_metadata( $attachment_id ); 370 | if ( isset( $image_meta['width'], $image_meta['height'] ) ) { 371 | // 'crop' is true so Photon's `resize` method is used 372 | $image_args = array( 373 | 'width' => $image_meta['width'], 374 | 'height' => $image_meta['height'], 375 | 'crop' => true 376 | ); 377 | } 378 | } 379 | 380 | // Expose determined arguments to a filter before passing to Photon. 381 | $transform = $image_args['crop'] ? 'resize' : 'fit'; 382 | 383 | // Check specified image dimensions and account for possible zero values; photon fails to resize if a dimension is zero. 384 | if ( 0 === $image_args['width'] || 0 === $image_args['height'] ) { 385 | if ( 0 === $image_args['width'] && 0 < $image_args['height'] ) { 386 | $photon_args['h'] = $image_args['height']; 387 | } elseif ( 0 === $image_args['height'] && 0 < $image_args['width'] ) { 388 | $photon_args['w'] = $image_args['width']; 389 | } 390 | } else { 391 | $image_meta = wp_get_attachment_metadata( $attachment_id ); 392 | if ( ( 'resize' === $transform ) && $image_meta ) { 393 | if ( isset( $image_meta['width'], $image_meta['height'] ) ) { 394 | // Lets make sure that we don't upscale images since wp never upscales them as well. 395 | $smaller_width = ( ( $image_meta['width'] < $image_args['width'] ) ? $image_meta['width'] : $image_args['width'] ); 396 | $smaller_height = ( ( $image_meta['height'] < $image_args['height'] ) ? $image_meta['height'] : $image_args['height'] ); 397 | 398 | $photon_args[ $transform ] = $smaller_width . ',' . $smaller_height; 399 | } 400 | } else { 401 | $photon_args[ $transform ] = $image_args['width'] . ',' . $image_args['height']; 402 | } 403 | } 404 | 405 | $photon_args = apply_filters( 'my_photon_image_downsize_string', $photon_args, compact( 'image_args', 'image_url', 'attachment_id', 'size', 'transform' ) ); 406 | 407 | // Generate Photon URL 408 | $image = array( 409 | my_photon_url( $image_url, $photon_args ), 410 | false, 411 | false 412 | ); 413 | } elseif ( is_array( $size ) ) { 414 | // Pull width and height values from the provided array, if possible 415 | $width = isset( $size[0] ) ? (int) $size[0] : false; 416 | $height = isset( $size[1] ) ? (int) $size[1] : false; 417 | 418 | // Don't bother if necessary parameters aren't passed. 419 | if ( ! $width || ! $height ) 420 | return $image; 421 | 422 | // Expose arguments to a filter before passing to Photon 423 | $photon_args = array( 424 | 'fit' => $width . ',' . $height 425 | ); 426 | 427 | $photon_args = apply_filters( 'my_photon_image_downsize_array', $photon_args, compact( 'width', 'height', 'image_url', 'attachment_id' ) ); 428 | 429 | // Generate Photon URL 430 | $image = array( 431 | my_photon_url( $image_url, $photon_args ), 432 | false, 433 | false 434 | ); 435 | } 436 | } 437 | 438 | return $image; 439 | } 440 | 441 | /** 442 | ** GENERAL FUNCTIONS 443 | **/ 444 | 445 | /** 446 | * Ensure image URL is valid for Photon. 447 | * Though Photon functions address some of the URL issues, we should avoid unnecessary processing if we know early on that the image isn't supported. 448 | * 449 | * @param string $url 450 | * @uses wp_parse_args 451 | * @return bool 452 | */ 453 | protected static function validate_image_url( $url ) { 454 | $base_url = My_Photon_Settings::get( 'base-url' ); 455 | $base_url_parsed = @parse_url( $base_url ); 456 | $parsed_url = @parse_url( $url ); 457 | 458 | if ( ! $parsed_url ) { 459 | return false; 460 | } 461 | 462 | // Parse URL and ensure needed keys exist, since the array returned by `parse_url` only includes the URL components it finds. 463 | $url_info = wp_parse_args( $parsed_url, array( 464 | 'scheme' => null, 465 | 'host' => null, 466 | 'port' => null, 467 | 'path' => null 468 | ) ); 469 | 470 | // Ensure port/protocol matches that of the image server. 471 | if ( $url_info['scheme'] !== $base_url_parsed['scheme'] 472 | || ( 'https' === $url_info['scheme'] 473 | && apply_filters( 'my_photon_reject_https', false ) 474 | ) 475 | ) { 476 | return false; 477 | } 478 | 479 | // Bail if no host is found 480 | if ( is_null( $url_info['host'] ) ) { 481 | return false; 482 | } 483 | 484 | // Bail if the image alredy went through Photon 485 | if ( false !== strpos( $base_url, $url_info['host'] ) ) { 486 | return false; 487 | } 488 | 489 | // Bail if no path is found 490 | if ( is_null( $url_info['path'] ) ) { 491 | return false; 492 | } 493 | 494 | // Ensure image extension is acceptable 495 | if ( ! in_array( 496 | strtolower( pathinfo( $url_info['path'], PATHINFO_EXTENSION ) ), 497 | self::$extensions 498 | ) ) { 499 | return false; 500 | } 501 | 502 | /** 503 | * Allow themes and plugins to add additional logic to determine 504 | * whether the URL should be Photonized. 505 | * 506 | * @param bool $is_valid Whether the image URL should be Photonized. 507 | * @param string $url The URL that is being examined. 508 | * @param array $parsed_url The URL as passed through parse_url(). 509 | */ 510 | return apply_filters( 511 | 'photon_validate_image_url', 512 | true, 513 | $url, 514 | $parsed_url 515 | ); 516 | } 517 | 518 | /** 519 | * Checks if the file exists before it passes the file to photon 520 | * 521 | * @param string $src The image URL 522 | * @return string 523 | **/ 524 | protected static function strip_image_dimensions_maybe( $src ) { 525 | $stripped_src = $src; 526 | 527 | // Build URL, first removing WP's resized string so we pass the original image to Photon 528 | if ( preg_match( '#(-\d+x\d+)\.(' . implode('|', self::$extensions ) . '){1}$#i', $src, $src_parts ) ) { 529 | $stripped_src = str_replace( $src_parts[1], '', $src ); 530 | $upload_dir = wp_upload_dir(); 531 | 532 | // Extracts the file path to the image minus the base url 533 | $file_path = substr( $stripped_src, strlen ( $upload_dir['baseurl'] ) ); 534 | 535 | if( file_exists( $upload_dir["basedir"] . $file_path ) ) 536 | $src = $stripped_src; 537 | } 538 | 539 | return $src; 540 | } 541 | 542 | /** 543 | * Provide an array of available image sizes and corresponding dimensions. 544 | * Similar to get_intermediate_image_sizes() except that it includes image sizes' dimensions, not just their names. 545 | * 546 | * @global $wp_additional_image_sizes 547 | * @uses get_option 548 | * @return array 549 | */ 550 | protected static function image_sizes() { 551 | if ( null == self::$image_sizes ) { 552 | global $_wp_additional_image_sizes; 553 | 554 | // Populate an array matching the data structure of $_wp_additional_image_sizes so we have a consistent structure for image sizes 555 | $images = array( 556 | 'thumb' => array( 557 | 'width' => intval( get_option( 'thumbnail_size_w' ) ), 558 | 'height' => intval( get_option( 'thumbnail_size_h' ) ), 559 | 'crop' => (bool) get_option( 'thumbnail_crop' ) 560 | ), 561 | 'medium' => array( 562 | 'width' => intval( get_option( 'medium_size_w' ) ), 563 | 'height' => intval( get_option( 'medium_size_h' ) ), 564 | 'crop' => false 565 | ), 566 | 'large' => array( 567 | 'width' => intval( get_option( 'large_size_w' ) ), 568 | 'height' => intval( get_option( 'large_size_h' ) ), 569 | 'crop' => false 570 | ), 571 | 'full' => array( 572 | 'width' => null, 573 | 'height' => null, 574 | 'crop' => false 575 | ) 576 | ); 577 | 578 | // Compatibility mapping as found in wp-includes/media.php 579 | $images['thumbnail'] = $images['thumb']; 580 | 581 | // Update class variable, merging in $_wp_additional_image_sizes if any are set 582 | if ( is_array( $_wp_additional_image_sizes ) && ! empty( $_wp_additional_image_sizes ) ) 583 | self::$image_sizes = array_merge( $images, $_wp_additional_image_sizes ); 584 | else 585 | self::$image_sizes = $images; 586 | } 587 | 588 | return is_array( self::$image_sizes ) ? self::$image_sizes : array(); 589 | } 590 | 591 | /** 592 | * Pass og:image URLs through Photon 593 | * 594 | * @param array $tags 595 | * @param array $parameters 596 | * @uses my_photon_url 597 | * @return array 598 | */ 599 | function filter_open_graph_tags( $tags, $parameters ) { 600 | if ( empty( $tags['og:image'] ) ) { 601 | return $tags; 602 | } 603 | 604 | $photon_args = array( 605 | 'fit' => sprintf( '%d,%d', 2 * $parameters['image_width'], 2 * $parameters['image_height'] ), 606 | ); 607 | 608 | if ( is_array( $tags['og:image'] ) ) { 609 | $images = array(); 610 | foreach ( $tags['og:image'] as $image ) { 611 | $images[] = my_photon_url( $image, $photon_args ); 612 | } 613 | $tags['og:image'] = $images; 614 | } else { 615 | $tags['og:image'] = my_photon_url( $tags['og:image'], $photon_args ); 616 | } 617 | 618 | return $tags; 619 | } 620 | } 621 | My_Photon::instance(); 622 | -------------------------------------------------------------------------------- /inc/functions.php: -------------------------------------------------------------------------------- 1 | '300', 'resize' => array( 123, 456 ) ), or in string form (w=123&h=456) 10 | * @return string The raw final URL. You should run this through esc_url() before displaying it. 11 | */ 12 | function my_photon_url( $image_url, $args = array(), $scheme = null ) { 13 | $image_url = trim( $image_url ); 14 | 15 | $image_url = apply_filters( 'my_photon_pre_image_url', $image_url, $args, $scheme ); 16 | $args = apply_filters( 'my_photon_pre_args', $args, $image_url, $scheme ); 17 | 18 | if ( empty( $image_url ) ) 19 | return $image_url; 20 | 21 | $image_url_parts = @parse_url( $image_url ); 22 | 23 | // Unable to parse 24 | if ( ! is_array( $image_url_parts ) || empty( $image_url_parts['host'] ) || empty( $image_url_parts['path'] ) ) 25 | return $image_url; 26 | 27 | if ( is_array( $args ) ){ 28 | // Convert values that are arrays into strings 29 | foreach ( $args as $arg => $value ) { 30 | if ( is_array( $value ) ) { 31 | $args[$arg] = implode( ',', $value ); 32 | } 33 | } 34 | 35 | // Encode values 36 | // See http://core.trac.wordpress.org/ticket/17923 37 | $args = rawurlencode_deep( $args ); 38 | } 39 | 40 | // You can't run a Photon URL through Photon again because query strings are stripped. 41 | // So if the image is already a Photon URL, append the new arguments to the existing URL. 42 | if ( false !== strpos( My_Photon_Settings::get( 'base-url' ), $image_url_parts['host'] ) ) { 43 | $photon_url = add_query_arg( $args, $image_url ); 44 | 45 | return my_photon_url_scheme( $photon_url, $scheme ); 46 | } 47 | 48 | // This setting is Photon Server dependent 49 | if ( ! apply_filters( 'my_photon_any_extension_for_domain', false, $image_url_parts['host'] ) ) { 50 | // Photon doesn't support query strings so we ignore them and look only at the path. 51 | // However some source images are served via PHP so check the no-query-string extension. 52 | // For future proofing, this is a blacklist of common issues rather than a whitelist. 53 | $extension = pathinfo( $image_url_parts['path'], PATHINFO_EXTENSION ); 54 | if ( empty( $extension ) || in_array( $extension, array( 'php' ) ) ) 55 | return $image_url; 56 | } 57 | 58 | $image_host_path = $image_url_parts['host'] . $image_url_parts['path']; 59 | 60 | $photon_url = My_Photon_Settings::get( 'base-url' ) . $image_host_path; 61 | 62 | // This setting is Photon Server dependent 63 | if ( isset( $image_url_parts['query'] ) && apply_filters( 'my_photon_add_query_string_to_domain', false, $image_url_parts['host'] ) ) { 64 | $photon_url .= '?q=' . rawurlencode( $image_url_parts['query'] ); 65 | } 66 | 67 | if ( $args ) { 68 | if ( is_array( $args ) ) { 69 | $photon_url = add_query_arg( $args, $photon_url ); 70 | } else { 71 | // You can pass a query string for complicated requests but where you still want CDN subdomain help, etc. 72 | $photon_url .= '?' . $args; 73 | } 74 | } 75 | 76 | return my_photon_url_scheme( $photon_url, $scheme ); 77 | } 78 | add_filter( 'my_photon_url', 'my_photon_url', 10, 3 ); 79 | 80 | 81 | function my_photon_url_scheme( $url, $scheme ) { 82 | if ( ! in_array( $scheme, array( 'http', 'https', 'network_path' ) ) ) { 83 | $scheme = is_ssl() ? 'https' : 'http'; 84 | } 85 | 86 | if ( 'network_path' == $scheme ) { 87 | $scheme_slashes = '//'; 88 | } else { 89 | $scheme_slashes = "$scheme://"; 90 | } 91 | 92 | return preg_replace( '#^[a-z:]+//#i', $scheme_slashes, $url ); 93 | } 94 | -------------------------------------------------------------------------------- /my-photon.php: -------------------------------------------------------------------------------- 1 | Use of this service is for users of the Jetpack by WordPress.com plugin only, 45 | > and may be used by sites hosted on WordPress.com, or on Jetpack-connected 46 | > WordPress sites. If you move to another platform, or disconnect Jetpack from 47 | > your site, we can't promise it will continue to work. 48 | 49 | == Changelog == 50 | 51 | = 0.1 = 52 | 53 | * Initial release --------------------------------------------------------------------------------