Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve the cloudinary url parser #704

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 10 additions & 31 deletions php/class-media.php
Original file line number Diff line number Diff line change
Expand Up @@ -645,34 +645,10 @@ public function get_public_id_from_url( $url, $as_sync_key = false ) {
if ( ! $this->is_cloudinary_url( $url ) ) {
return null;
}

$path = wp_parse_url( $url, PHP_URL_PATH );
$parts = explode( '/', ltrim( $path, '/' ) );

$maybe_seo = array();

// Need to find the version part as anything after this is the public id.
foreach ( $parts as $part ) {
$maybe_seo[] = array_shift( $parts ); // Get rid of the first element.
if ( 'v' === substr( $part, 0, 1 ) && is_numeric( substr( $part, 1 ) ) ) {
break; // Stop removing elements.
}
}

// The remaining items should be the file.
$file = implode( '/', $parts );
$path_info = Utils::pathinfo( $file );

// Is SEO friendly URL.
if ( in_array( 'images', $maybe_seo, true ) ) {
$public_id = $path_info['dirname'];
} else {
$public_id = isset( $path_info['dirname'] ) && '.' !== $path_info['dirname'] ? $path_info['dirname'] . DIRECTORY_SEPARATOR . $path_info['filename'] : $path_info['filename'];
}
$public_id = trim( $public_id, './' );


$public_id = Utils::parse_url( $url, CLOUDINARY_URL_PUBLIC_ID );
if ( $as_sync_key ) {
$transformations = $this->get_transformations_from_string( $url );
$transformations = Utils::parse_url( $url, CLOUDINARY_URL_TRANSFORMATIONS_PARSED );
$public_id .= ! empty( $transformations ) ? wp_json_encode( $transformations ) : '';
}

Expand Down Expand Up @@ -1263,7 +1239,7 @@ public function cloudinary_url( $attachment_id, $size = array(), $transformation

// Make a copy as not to destroy the options in \Cloudinary::cloudinary_url().
$args = $pre_args;
$url = $this->plugin->components['connect']->api->cloudinary_url( $cloudinary_id, $args, $set_size );
$url = $this->plugin->components['connect']->api->cloudinary_url( $cloudinary_id, $args, $set_size, $attachment_id );

// Check if this type is a preview only type. i.e PDF.
if ( ! empty( $set_size ) && $this->is_preview_only( $attachment_id ) ) {
Expand Down Expand Up @@ -1782,10 +1758,13 @@ public function is_cloudinary_url( $url ) {
if ( ! filter_var( utf8_uri_encode( $url ), FILTER_VALIDATE_URL ) ) {
return false;
}
$test_parts = wp_parse_url( $url );
$cld_url = $this->plugin->components['connect']->api->asset_url;
$test_parts = wp_parse_url( $url );
$cloudinary_hosts = array(
$this->plugin->components['connect']->api->asset_url,
Utils::CLOUDINARY_HOST,
);

return isset( $test_parts['path'] ) && $test_parts['host'] === $cld_url;
return isset( $test_parts['path'] ) && in_array( $test_parts['host'], $cloudinary_hosts, true );
}

/**
Expand Down
71 changes: 71 additions & 0 deletions php/class-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,77 @@ protected function setup_endpoints() {
if ( ! defined( 'CLOUDINARY_ENDPOINTS_VIDEO_PLAYER_VERSION' ) ) {
define( 'CLOUDINARY_ENDPOINTS_VIDEO_PLAYER_VERSION', '1.5.1' );
}

/**
* The Cloudinary URL public ID.
*/
if ( ! defined( 'CLOUDINARY_URL_PUBLIC_ID' ) ) {
define( 'CLOUDINARY_URL_PUBLIC_ID', 8 );
}

/**
* The Cloudinary URL version.
*/
if ( ! defined( 'CLOUDINARY_URL_VERSION' ) ) {
define( 'CLOUDINARY_URL_VERSION', 9 );
}

/**
* The Cloudinary URL transformations.
*/
if ( ! defined( 'CLOUDINARY_URL_TRANSFORMATIONS' ) ) {
define( 'CLOUDINARY_URL_TRANSFORMATIONS', 10 );
}

/**
* The Cloudinary URL parsed transformations.
*/
if ( ! defined( 'CLOUDINARY_URL_TRANSFORMATIONS_PARSED' ) ) {
define( 'CLOUDINARY_URL_TRANSFORMATIONS_PARSED', 11 );
}

/**
* The Cloudinary URL asset type.
*/
if ( ! defined( 'CLOUDINARY_URL_ASSET_TYPE' ) ) {
define( 'CLOUDINARY_URL_ASSET_TYPE', 12 );
}

/**
* The Cloudinary URL delivery.
*/
if ( ! defined( 'CLOUDINARY_URL_DELIVERY' ) ) {
define( 'CLOUDINARY_URL_DELIVERY', 13 );
}

/**
* The Cloudinary URL format.
*/
if ( ! defined( 'CLOUDINARY_URL_FORMAT' ) ) {
define( 'CLOUDINARY_URL_FORMAT', 14 );
}

/**
* The Cloudinary URL query parsed.
*/
if ( ! defined( 'CLOUDINARY_URL_QUERY_PARSED' ) ) {
define( 'CLOUDINARY_URL_QUERY_PARSED', 15 );
}

/**
* The WordPress attachment ID.
*/
if ( ! defined( 'CLOUDINARY_URL_ATTACHMENT_ID' ) ) {
define( 'CLOUDINARY_URL_ATTACHMENT_ID', 16 );
}

/**
* The WordPress attachment object.
*/
if ( ! defined( 'CLOUDINARY_URL_ATTACHMENT' ) ) {
define( 'CLOUDINARY_URL_ATTACHMENT', 17 );
}

}

/**
Expand Down
186 changes: 186 additions & 0 deletions php/class-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Cloudinary;

use Cloudinary\Connect\Api;
use Cloudinary\Settings\Setting;
use Google\Web_Stories\Story_Post_Type;

Expand All @@ -17,6 +18,11 @@
*/
class Utils {

/**
* Holds the default/public Cloudinary host.
*/
const CLOUDINARY_HOST = 'res.cloudinary.com';

/**
* Filter an array recursively
*
Expand Down Expand Up @@ -432,4 +438,184 @@ public static function pathinfo( $path, $flags = 15 ) {

return is_array( $pathinfo ) ? array_map( 'urldecode', $pathinfo ) : urldecode( $pathinfo );
}

/**
* Check if a delivery type is Cloudinary supported.
*
* @param string $type The type to check.
*
* @return bool
*/
public static function is_delivery_type( $type ) {
$types = array(
'upload',
'private',
'authenticated',
'list',
'fetch',
'facebook',
'twitter',
'twitter_name',
'gravatar',
'youtube',
'hulu',
'vimeo',
'animoto',
'worldstarhiphop',
'dailymotion',
'multi ',
'text',
'sprite',
);

return in_array( $type, $types, true );
}

/**
* Parse a Cloudinary URL into components.
*
* @param string $url The URL to parse.
* @param int $component The flag of a single component to get.
*
* @return array|mixed|object|null
*/
public static function parse_url( $url, $component = - 1 ) {

static $api, $media, $ext_types, $urls, $globals;

$defaults = array(
'scheme' => null,
'host' => null,
'port' => null,
'user' => null,
'pass' => null,
'path' => null,
'query' => null,
'fragment' => null,
'public_id' => null,
'version' => 1,
'transformations' => null,
'transformations_parsed' => array(),
'asset_type' => null,
'delivery' => null,
'format' => null,
'query_parsed' => array(),
'attachment_id' => 0,
'attachment' => null,
);

if ( ! isset( $urls[ $url ] ) ) {
if ( ! $api ) {
$media = get_plugin_instance()->get_component( 'media' );
$connect = get_plugin_instance()->get_component( 'connect' );
$api = $connect->api;
$types = wp_get_ext_types();
$ext_types = array_merge( $types['image'], $types['audio'], $types['video'] );
$globals = array(
'image' => $media->apply_default_transformations( array(), 'image' ),
'video' => $media->apply_default_transformations( array(), 'video' ),
);
}
$is_seo = false;
$components = wp_parse_args( wp_parse_url( $url ), $defaults );

if ( ! $api || ! $media->is_cloudinary_url( $url ) ) {
// Not connected. Return as per normal.
return $components;
}
$parts = explode( '/', trim( $components['path'], '/' ) );

/**
* Allocate the parts according to the Cloudinary URL structure.
*
* @see https://cloudinary.com/documentation/image_transformations#transformation_url_structure
*/
if ( self::CLOUDINARY_HOST === $components['host'] ) {
array_shift( $parts );
}

// Type and Delivery.
$components['asset_type'] = array_shift( $parts );
if ( 'images' === $components['asset_type'] ) {
$components['asset_type'] = 'image';
$components['delivery'] = 'upload';
$is_seo = true;
} else {
$maybe_delivery_type = array_shift( $parts );
if ( self::is_delivery_type( $maybe_delivery_type ) ) {
$components['delivery'] = $maybe_delivery_type;
}
}

// If we don't have a delivery type at this point, the URL is not a proper constructed Cloudinary URL.
if ( $components['delivery'] ) {
// Transformations.
$has_transformations = $media->get_transformations_from_string( ltrim( $components['path'], '/' ), $components['asset_type'] );
// Remove transformations string if we have any.
if ( ! empty( $has_transformations ) ) {
$type_global = $globals[ $components['asset_type'] ];
$transformations = Api::generate_transformation_string( $has_transformations, $components['asset_type'] );
$transformation_parts = explode( '/', $transformations );
$new_parts = array_diff( $parts, $transformation_parts );
$parts = array_values( $new_parts ); // Reset the keys since array_diff keeps the indexes.
$type_global['size'] = $media->get_crop_from_transformation( $has_transformations );
$has_transformations = array_filter(
$has_transformations,
function ( $item ) use ( $type_global ) {

return ! in_array( $item, $type_global, true );
}
);
if ( ! empty( $has_transformations ) ) {
$components['transformations'] = Api::generate_transformation_string( $has_transformations, $components['asset_type'] );
$components['transformations_parsed'] = $has_transformations;
}
}

// Version.
if ( 'v' === substr( $parts[0], 0, 1 ) && is_numeric( substr( $parts[0], 1 ) ) ) {
$components['version'] = array_shift( $parts );
}

// Get public_id.
$cloudinary_id = implode( '/', $parts ); // Cloudinary ID includes the extension.
$components['public_id'] = $cloudinary_id;
$ext_pos = strrpos( $cloudinary_id, '.' );
if ( $ext_pos ) {
$format_maybe = substr( $cloudinary_id, $ext_pos + 1 );
if ( in_array( $format_maybe, $ext_types, true ) ) {
$components['format'] = $format_maybe;
$components['public_id'] = substr( $cloudinary_id, 0, $ext_pos );
}
}
if ( true === $is_seo ) {
// Check if we have a wp-image-{id}-.
if ( preg_match( '/:wp-image-(\d+):/', '/' . basename( $components['public_id'] ), $match ) ) {
$components['attachment_id'] = intval( $match[1] );
}
$components['public_id'] = dirname( $components['public_id'] );
}
}

if ( ! empty( $components['query'] ) ) {
wp_parse_str( $components['query'], $components['query_parsed'] );
}
$urls[ $url ] = array_filter( $components );
}

$components = $urls[ $url ];

// Return the single component if one is specified.
if ( 0 <= $component ) {
$keys = array_keys( $defaults );
$key = $keys[ $component ];
$components = isset( $urls[ $url ][ $key ] ) ? $urls[ $url ][ $key ] : null;
// Get the post if requested.
if ( 17 === $component && isset( $urls[ $url ]['attachment_id'] ) ) {
$components = get_post( $urls[ $url ]['attachment_id'] );
}
}

return $components;
}
}
Loading