Как urlencode только часть url-адреса?

У меня есть веб-сайт WordPress с двумя настраиваемыми типами сообщений: book и article.

Из-за требований к дизайну я хочу создать шорткод для return url избранного изображения публикации. Я не мог использовать встроенную функцию get_the_post_thumbnail_url(), потому что мои изображения кэшированы в wordpress.com сети (с использованием Jetpack), и эта функция возвращает кэшированный url, что не соответствует моим требованиям к дизайну.

Я создал следующий шорткод:

function wp_featured_image_url( $atts ) {
   global $post;
    $url = get_the_post_thumbnail_url($post->ID); 
    if (is_singular($post_types = 'book')) {      
        if ( has_post_thumbnail() )  {
                return $url;    
                    }
        else {
                echo 'https://example.com/wp-content/uploads/year/month/generic-featured-image-for-books.png';  
            }   
        }

    else
        if (is_singular($post_types = 'article')) {
            if ( has_post_thumbnail() )  {
                    return $url;    
                    }
            else {
                echo 'https://example.com/wp-content/uploads/year/month/generic-featured-image-for-articles.png';   
                }   
        }       

}
add_shortcode( 'featured_image_url', 'wp_featured_image_url' );

Он работает нормально, за исключением того, что заголовки файлов изображений на арабском языке (UTF-8), поэтому эта функция вернет url, например:

https://example.com/wp-content/uploads/2019/10/كلام-عربي-كتير.jpg

Я хочу, чтобы он возвращал только urlencode имени файла изображения. Однако, если я изменил return $url; на return urlencode($url); или return rawurlencode($url);, он вернет весь URL как urlencode:

https%3A%2F%2Fexample.com%2Fwp-content%2Fuploads%2F2019%2F10%2F%D9%83%D9%84%D8%A7%D9%85-%D8%B9%D8%B1%D8%A8%D9%8A-%D9%83%D8%AA%D9%8A%D8%B1.jpg

Я хочу иметь возможность настроить функцию так, чтобы первая часть url оставалась нетронутой, и только имя файла изображения получало urlencode, например:

https://example.com/wp-content/uploads/2019/10/%D9%83%D9%84%D8%A7%D9%85-%D8%B9%D8%B1%D8%A8%D9%8A-%D9%83%D8%AA%D9%8A%D8%B1.jpg

Может кто-нибудь указать мне правильное направление? Заранее спасибо.


person Atef Wagih    schedule 16.12.2019    source источник


Ответы (4)


Разделите URL-адрес на две части: первая - все от начала до последней косой черты, а вторая - все остальное без косой черты до конца. Затем объедините их обратно, кодируя только вторую часть.

<?php

  function url_onlyfile_encode($url) {
    if (preg_match('#^(.*/)([^/]+)$#u', $url, $res)) {
      return $res[1] . urlencode($res[2]);
    }
    return urlencode($url);
  }

  // test
  $s = 'https://example.com/wp-content/uploads/2019/10/كلام-عربي-كتير.jpg';
  print url_onlyfile_encode($s);
  // https://example.com/wp-content/uploads/2019/10/%D9%83%D9%84%D8%A7%D9%85-%D8%B9%D8%B1%D8%A8%D9%8A-%D9%83%D8%AA%D9%8A%D8%B1.jpg

ОБНОВЛЕНО

Если вам нужен самый быстрый код, попробуйте работать напрямую со строками, например:

  function url_onlyfile_encode($url) {
    $p = strrpos($url, '/'); // Find the last slash
    if ($p !== false) {
      // Encode only the part after the last slash
      return substr($url, 0, $p + 1) . urlencode(substr($url, $p + 1)); 
    } else {
      return urlencode($url);
    }
  }

Я провел простой тест с такими циклами:

  // test string
  $s = 'https://example.com/wp-content/uploads/2019/10/كلام-عربي-كتير.jpg';

  $repeat_count = 1000000;

  $tm = microtime(true);
  for ($i = 0 ; $i < $repeat_count ; $i++) {
    tested_func($s);
  }
  $tm = microtime(true) - $tm;
  print "Time: " . round($tm * 1000) . " ms" . PHP_EOL;

Полный код находится здесь

Результаты приведены ниже:

php 5.6.40:

(Пусто): 17 мс

AterLux (регулярное выражение): 1907 мс

AterLux (str): 641 мс

Эмануэль: 3583 мс

Петтер Харсем: 1269 мс

Yeeooow: 1884 мс

(Примечание: «Пустой» - это пустой цикл без вызова функций внутри)

PHP 7.3.4 (x64):

(Пусто): 9 мс

AterLux (регулярное выражение): 499 мс

AterLux (str): 284 мс

Эмануэль: 2820 мс

Петтер Харсем: 477 мс

Ууууу: 804 мс

Как видите, вариант с strrpos в любом случае самый быстрый.

Среди прочего, Petter Harsem's explode -> count -> implode показывает лучшие результаты, чем регулярные выражения, хотя в php7 разница не такая большая.

Ответ Yeeooow, который также использует explode и count, но использует for-loop для обратной сборки строки, работает быстрее, чем регулярные выражения на php5, но показывает в два раза больше времени на php7.

Ответ Эмануэля, который включает вызов parse_url, занимает больше всего времени.

В любом случае разница составляет пару микросекунд, что в реальном мире незначительно.

person AterLux    schedule 16.12.2019
comment
Хм, я не знал, что вы можете разделить регулярное выражение, используя # - person Flame; 16.12.2019
comment
Спасибо AterLux. Просто интересно, есть ли предпочтения по производительности между вашим решением и решением @Peter Harsem ниже? У меня тысячи постов. Есть ли решение, которое лучше других при реализации в большом масштабе? - person Atef Wagih; 17.12.2019
comment
@AtefWagih Я потратил ответ - person AterLux; 17.12.2019

Другой способ был бы грубым, но более понятным.

<?php
$url = "https://example.com/wp-content/uploads/2019/10/كلام-عربي-كتير.jpg";
$e = explode('/', $url); // seperate it ou using the /
$c = count($e); // get the number of elements
$ne = $c - 1; // the last element
$file = $e[$ne]; // كلام-عربي-كتير.jpg
$newURL = ''; // empty var
for ($i = 0; $i < $ne; $i++) {
    $newURL .= $e[$i] . '/'; // rebuild the url
}
echo $newURL . urlencode($file); //put it all togeather again

ответ будет

https://example.com/wp-content/uploads/2019/10/%D9%83%D9%84%D8%A7%D9%85-%D8%B9%D8%B1%D8%A8%D9%8A-%D9%83%D8%AA%D9%8A%D8%B1.jpg 
person Yeeooow    schedule 16.12.2019

Короче говоря, вам нужно разделить URL-адрес, применить urlencode к имени файла, а затем снова собрать URL-адрес. Есть несколько способов сделать это, например, с помощью регулярного выражения, чтобы выбрать часть после последнего /.

Или другое простое решение с использованием explode и _ 4_:

// Split the URL on '/'
$urlParts = explode('/', $url);

// URL encode the last part
$numParts = count($urlParts);
$urlParts[$numParts - 1] = urlencode($urlParts[$numParts - 1]);

// Put the parts back together to a string
$formattedUrl = implode('/', $urlParts);
person Petter Harsem    schedule 16.12.2019

Попробуй это:

$url = "https://example.com/wp-content/uploads/2019/10/كلام-عربي-كتير.jpg";
$parsed = parse_url($url);
$pathFragments = explode('/', $parsed['path']);
$fileAndExt = array_pop($pathFragments);
list($file, $ext) = explode(".", $fileAndExt);

return $parsed['scheme']."://".$parsed['host'].implode("/", $pathFragments)."/".urlencode($file).".".$ext;
person Emanuele    schedule 16.12.2019