Extract shortcode parameters in content - Wordpress - php

Think about a post content like below:
[shortcode a="a_param"]
... Some content and shortcodes here
[shortcode b="b_param"]
.. Again some content here
[shortcode c="c_param"]
I have a shortcode that takes 3 or more parameters.
I want to find out how many times the shortcode is used at the content and its parameters in an array like,
array (
[0] => array(a => a_param, b=> null, c=>null),
[1] => array(a => null, b=> b_param, c=>null),
[2] => array(a => null, b=> null, c=>c_param),
)
I need to do this in the_content filter, wp_head filter or something similar.
How can I do this ?
Thank you,

In wordpress get_shortcode_regex() function returns regular expression used to search for shortcodes inside posts.
$pattern = get_shortcode_regex();
Then preg_match the pattern with the post content
if ( preg_match_all( '/'. $pattern .'/s', $post->post_content, $matches ) )
If it return true, then extracted shortcode details are saved in $matches variable.
Try
global $post;
$result = array();
//get shortcode regex pattern wordpress function
$pattern = get_shortcode_regex();
if ( preg_match_all( '/'. $pattern .'/s', $post->post_content, $matches ) )
{
$keys = array();
$result = array();
foreach( $matches[0] as $key => $value) {
// $matches[3] return the shortcode attribute as string
// replace space with '&' for parse_str() function
$get = str_replace(" ", "&" , $matches[3][$key] );
parse_str($get, $output);
//get all shortcode attribute keys
$keys = array_unique( array_merge( $keys, array_keys($output)) );
$result[] = $output;
}
//var_dump($result);
if( $keys && $result ) {
// Loop the result array and add the missing shortcode attribute key
foreach ($result as $key => $value) {
// Loop the shortcode attribute key
foreach ($keys as $attr_key) {
$result[$key][$attr_key] = isset( $result[$key][$attr_key] ) ? $result[$key][$attr_key] : NULL;
}
//sort the array key
ksort( $result[$key]);
}
}
//display the result
print_r($result);
}

Just to save time, this is the function I made based on #Tamil Selvan C answer :
function get_shortcode_attributes( $shortcode_tag ) {
global $post;
if( has_shortcode( $post->post_content, $shortcode_tag ) ) {
$output = array();
//get shortcode regex pattern wordpress function
$pattern = get_shortcode_regex( [ $shortcode_tag ] );
if ( preg_match_all( '/'. $pattern .'/s', $post->post_content, $matches ) )
{
$keys = array();
$output = array();
foreach( $matches[0] as $key => $value) {
// $matches[3] return the shortcode attribute as string
// replace space with '&' for parse_str() function
$get = str_replace(" ", "&" , trim( $matches[3][$key] ) );
$get = str_replace('"', '' , $get );
parse_str( $get, $sub_output );
//get all shortcode attribute keys
$keys = array_unique( array_merge( $keys, array_keys( $sub_output )) );
$output[] = $sub_output;
}
if( $keys && $output ) {
// Loop the output array and add the missing shortcode attribute key
foreach ($output as $key => $value) {
// Loop the shortcode attribute key
foreach ($keys as $attr_key) {
$output[$key][$attr_key] = isset( $output[$key] ) && isset( $output[$key] ) ? $output[$key][$attr_key] : NULL;
}
//sort the array key
ksort( $output[$key]);
}
}
}
return $output;
}else{
return false;
}
}

Related

Include html href code to shotcode with "show content on a date"

I want to use a shortcode to show determinate content depending on a date given, I have this code on function.php on WP:
add_shortcode( 'stime', 'stime_f' );
function stime_f( $atts, $content = '' ) {
$a = shortcode_atts( [
'type' => false,
], $atts );
$output = '';
if ( $a['type'] ) {
$a['type'] = array_map( 'trim', str_getcsv( $a['type'], ',' ) );
}
foreach ($a as $value){
$list0 = $value[0];
$list1 = $value[1];
}
$tm = strtotime($list1);
if ( time() < $tm ) {
$list = "SOON";
return $list;
}
$list = $list0;
return $list;
}
So I used when publish:
[stime type="http://site1.com
http://site2.com,2020-12-05"]
And works, when it's not the date give result: SOON
otherwise result:
http://site1.com
http://site2.com
Results are fine, but I want to use:
[stime type="SITE1
SITE2,2020-12-05"]
I suppose it's for quotes on href and target, there are some way to make it possible?
Thanks
You can use enclosing shortcodes for this.
Below is the updated code:
add_shortcode( 'stime', 'stime_f' );
function stime_f( $atts, $content ) {
if ( $content ) {
$a = array_map( 'trim', str_getcsv( $content, ',' ) );
$list0 = $a[0];
$list1 = $a[1];
$tm = strtotime($list1);
if ( time() < $tm ) {
$list = "SOON";
return $list;
}
$list = $list0;
return $list;
} else {
return '';
}
}
Then use shortcode like this:
[stime] SITE1
SITE2,2020-12-05 [/stime]

Cut a string/url to always get a final string/url with a specific data and it's value in php

I have an url that contain the word "&key".
The "&key" word can be at the beginning or at the end of our url.
Ex1= http://xxxxx.com?c1=xxx&c2=xxx&c3=xxx&key=xxx&c4=xxx&f1=xxx
Ex2= http://xxxxx.com?c1=xxx&key=xxx&c2=xxx&c3=xxx&c4=xxx&f1=xxx
What I would like to get is all the time the url with the Key element and it's value.
R1: http://xxxxx.com?c1=xxx&c2=xxx&c3=xxx&key=xxx
R2: http://xxxxx.com?c1=xxx&key=xxx
Here is what I have done:
$lp_sp_ad_publisher = "http://xxxxx.com?c1=xxx&c2=xxx&c3=xxx&key=xxxc4=xxxf1=xxx";
$lp_sp_ad_publisher_cut_link = explode("&", $lp_sp_ad_publisher_cut[1]); // tab
$lp_sp_ad_publisher_cut_link_final = $lp_sp_ad_publisher_cut_link[0]; // http://xxxxx.com?c1=xxx
$counter = 1;
// finding &key inside $lp_sp_ad_publisher_cut_link_final
while ((strpos($lp_sp_ad_publisher_cut_link_final, '&key')) !== false);
{
$lp_sp_ad_publisher_cut_link_final .= $lp_sp_ad_publisher_cut_link[$counter];
echo 'counter: ' . $counter . ' link: ' . $lp_sp_ad_publisher_cut_link_final . '<br/>';
$counter++;
}
I'm only looping once all the time. I guess the while loop isn't refreshing with the inside new value. Any solution?
EDIT: Sorry, I misunderstood the question.
This is tricky because the url key and value can be anything, so it might be safer to breakdown the URL using a combination of parse_url() and parse_str(), then put the url back together leaving off the part you don't want. Something like this:
function cut_url( $url='', $key='' )
{
$output = '';
$parts = parse_url( $url );
$query = array();
if( isset( $parts['scheme'] ) )
{
$output .= $parts['scheme'].'://';
}
if( isset( $parts['host'] ) )
{
$output .= $parts['host'];
}
if( isset( $parts['path'] ) )
{
$output .= $parts['path'];
}
if( isset( $parts['query'] ) )
{
$output .= '?';
parse_str( $parts['query'], $query );
}
foreach( $query as $qkey => $qvalue )
{
$output .= $qkey.'='.$qvalue.'&';
if( $qkey == $key ) break;
}
return rtrim( $output, '&' );
}
Usage:
$input = 'https://www.xxxxx.com/test/path/index.php?c1=xxx&c2=xxx&key=xxx&c3=xxx&c4=xxx&f1=xxx';
$output = cut_url( $input, 'key' );
Output:
https://www.xxxxx.com/test/path/index.php?c1=xxx&c2=xxx&key=xxx
If the intention is to always ensure that the parameter key and it's associated value appear at the end of the string, how about something like:
$tmp=array();$key='';
$parts=explode( '&', parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ) );
foreach( $parts as $pair ) {
list( $param,$value )=explode( '=',$pair );
if( $param=='key' )$key=$pair;
else $tmp[]=$pair;
}
$query = implode( '&', array( implode( '&', $tmp ), $key ) );
echo $query;
or,
parse_str( $_SERVER['QUERY_STRING'], $pieces );
foreach( $pieces as $param => $value ){
if( $param=='key' ) $key=$param.'='.$value;
else $tmp[]=$param.'='.$value;
}
$query = implode( '&', array( implode( '&', $tmp ), $key ) );
update
I'm puzzled that you were "not getting the good result"!
consider the url:
https://localhost/index.php?sort=0&dir=false&tax=23&cost=99&aardvark=creepy&key=banana&tree=large&ac=dc&limit=1000#569f945674935
The above would output:
sort=0&dir=false&tax=23&cost=99&aardvark=creepy&tree=large&ac=dc&limit=1000&key=banana
so the key=banana gets placed last using either method above.

Remove exact embed code(wordpress) with preg_replace

I'm using the following code to find first YouTube/Vimeo embed in the post content:
function compare_by_offset( $a, $b ) {
return $a['order'] - $b['order'];
}
function first_video_url($post_id = null) {
if ( $post_id == null OR $post_id == '' ) $post_id = get_the_ID();
$post_array = get_post( $post_id );
$markup = $post_array->post_content;
$regexes = array(
'#(?:https?:)?//www\.youtube(?:\-nocookie)?\.com/(?:v|e|embed)/([A-Za-z0-9\-_]+)#', // Comprehensive search for both iFrame and old school embeds
'#(?:https?(?:a|vh?)?://)?(?:www\.)?youtube(?:\-nocookie)?\.com/watch\?.*v=([A-Za-z0-9\-_]+)#', // Any YouTube URL. After http(s) support a or v for Youtube Lyte and v or vh for Smart Youtube plugin
'#(?:https?(?:a|vh?)?://)?youtu\.be/([A-Za-z0-9\-_]+)#', // Any shortened youtu.be URL. After http(s) a or v for Youtube Lyte and v or vh for Smart Youtube plugin
'#<div class="lyte" id="([A-Za-z0-9\-_]+)"#', // YouTube Lyte
'#data-youtube-id="([A-Za-z0-9\-_]+)"#', // LazyYT.js
'#<object[^>]+>.+?http://vimeo\.com/moogaloop.swf\?clip_id=([A-Za-z0-9\-_]+)&.+?</object>#s', // Standard Vimeo embed code
'#(?:https?:)?//player\.vimeo\.com/video/([0-9]+)#', // Vimeo iframe player
'#\[vimeo id=([A-Za-z0-9\-_]+)]#', // JR_embed shortcode
'#\[vimeo clip_id="([A-Za-z0-9\-_]+)"[^>]*]#', // Another shortcode
'#\[vimeo video_id="([A-Za-z0-9\-_]+)"[^>]*]#', // Yet another shortcode
'#(?:https?://)?(?:www\.)?vimeo\.com/([0-9]+)#', // Vimeo URL
'#(?:https?://)?(?:www\.)?vimeo\.com/channels/(?:[A-Za-z0-9]+)/([0-9]+)#' // Channel URL
);
$provider_videos = array();
foreach ( $regexes as $regex ) {
if ( preg_match_all( $regex, $markup, $matches, PREG_OFFSET_CAPTURE ) ) {
$provider_videos = array_merge( $provider_videos, $matches[0] );
}
}
if ( empty( $provider_videos ) ) return;
foreach ( $provider_videos as $video ) {
$videos[] = array(
'url' => $video[0],
'order' => $video[1]
);
}
usort( $videos, 'compare_by_offset' );
$first_video_url = current(array_column($videos, 'url'));
if ( empty( $first_video_url ) ) return;
return $first_video_url;
}
Now when I got the link to the first video in the post I want to remove it from the post content. And that's where I'm stuck. My attempt so far:
function remove_first_image ($content) {
$url = first_video_url();
$parsed = parse_url($url);
$video_id = $parsed['query'];
$embed_code = wp_oembed_get($url);
$pattern = 'a pattern for that embed which I fail to make';
$content = preg_replace($pattern, '', $content);
return $content;
}
add_filter('the_content', 'remove_first_image');
Thanks!
I guess one couldn't answer his own stupid question until he asks it. Here comes the answer:
function remove_first_image ($content) {
if ( is_single() && has_post_format('video') ) {
$url = first_video_url();
$embed_code = wp_oembed_get($url);
$content = str_replace($embed_code, '', $content);
}
return $content;
}
add_filter('the_content', 'remove_first_image');

I call a function in itself recursively, but it doesn't seem to be working

I am calling a crawl() function recursively that gets the content of a page and echo's it.
It works when I manually call it from outside the function, but when I recursively call it from inside the function I get no output from the recursive calls. The only output I get is from the one manual call.
Why isn't this working, what am I doing wrong?
<?php
error_reporting( E_ERROR );
define( "CRAWL_LIMIT_PER_DOMAIN", 50 );
$domains = array();
$urls = array();
$dom = new DOMDocument();
$matches = array();
function crawl( $domObject, $url, $matchList )
{
global $domains, $urls;
$parse = parse_url( $url );
$domains[ $parse['host'] ]++;
$urls[] = $url;
$content = file_get_contents( $url );
if ( $content === FALSE ){
return;
}
echo strip_tags($content) . "<br /><br /><br />";
array_push($matchList, 'http://www.the-irf.com/hello/hello5.html');
array_push($matchList, 'http://www.the-irf.com/hello/hello6.html');
array_push($matchList, 'http://www.the-irf.com/hello/end.html');
foreach( $matchList[0] as $crawled_url ) {
$parse = parse_url( $crawled_url );
if ( count( $domains[ $parse['host'] ] ) < CRAWL_LIMIT_PER_DOMAIN && !in_array( $crawled_url, $urls ) ) {
sleep( 1 );
crawl( $domObject, $crawled_url, $matchList );
}
}
}
crawl($dom, 'http://the-irf.com/hello/hello6.html', $matches);
?>
The problem lies when you are using your foreach.
foreach($matchList[0] as $crawled_url){
Your array $matchList looks like: array('http://www.the-irf.com/hello/hello5.html', 'http://www.the-irf.com/hello/hello6.html', 'http://www.the-irf.com/hello/end.html').
foreach is expecting the first parameter to be an array. $matchList[0] is not an array but the string 'http://www.the-irf.com/hello/hello5.html'.
In other words, if you change that line to
foreach($matchList as $crawled_url){
you will start calling the function recursively.

Magento : Issue Category name is auto renaming while saving

i am importing the products with advance data flow profile and facing the weird problem while saving the category as i am passing the category name to my function as
Parent Category/Child category
The / sign between categories automatically create and assign the product to child category
it is working as expected but in my case the Parent Category name is renaming somehow i have checked that i am passing the right name to the function...
e.g. Semipreciuos gem stone beads/Stone type is saving as Semipreciuos gem stone bead/Stone type
The last s word is missing from the name
protected function _addCategories($categories,$desc='',$discountable,$store) {
$rootId = $store->getRootCategoryId ();
if (! $rootId) {
return array();
}
$rootPath = '1/' . $rootId;
if (empty ( $this->_categoryCache [$store->getId ()] )) {
$collection = Mage::getModel ( 'catalog/category' )->getCollection ()->setStore($store)->addAttributeToSelect ( 'name' );
$collection->getSelect ()->where ( "path like '" . $rootPath . "/%'" );
foreach ( $collection as $cat ) {
$pathArr = explode ( '/', $cat->getPath () );
$namePath = '';
for($i = 2, $l = sizeof ( $pathArr ); $i < $l; $i ++) {
$name = $collection->getItemById ( $pathArr [$i] )->getName ();
$namePath .= (empty ( $namePath ) ? '' : '/') . trim ( $name );
}
$cat->setNamePath ( $namePath );
}
$cache = array();
foreach ( $collection as $cat ) {
$cache [strtolower ( $cat->getNamePath () )] = $cat;
$cat->unsNamePath ();
}
$this->_categoryCache [$store->getId ()] = $cache;
}
$cache = & $this->_categoryCache [$store->getId ()];
$catIds = array();
foreach ( explode ( ',', $categories ) as $categoryPathStr ) {
$categoryPathStr = preg_replace ( '#s*/s*#', '/', trim ( $categoryPathStr ) );
if (! empty ( $cache [$categoryPathStr] )) {
$catIds [] = $cache [$categoryPathStr]->getId ();
continue;
}
$path = $rootPath;
$namePath = '';
foreach ( explode ( '/', $categoryPathStr ) as $catName ) {
$namePath .= (empty ( $namePath ) ? '' : '/') . strtolower ( $catName );
if (empty ( $cache [$namePath] )) {
$cat = Mage::getModel('catalog/category')->setStoreId($store->getId())->setPath ( $path )->setName ( $catName )->// comment out the following line if new categories should stay inactive
setIsActive(1)->setDescription($desc)->setData('discountable',$discountable)->save();
$cache [$namePath] = $cat;
}
$catId = $cache [$namePath]->getId ();
$path .= '/' . $catId;
}
##Assigning product to child category
/*$parentId = null;
$currentcat = Mage::getModel("catalog/category")->load($catId);
$parentId = $currentcat->getParentId();
if($parentId){
$catIds[] = $parentId;
}
*/
if ($catId) {
$catIds [] = $catId;
}
}
return join ( ',', $catIds );
}
Above is my category function for creating categories... any one has any idea what is going on..
It is not related to Magento, but rather to PHP & regular expression.
$categoryPathStr = preg_replace ( '#s*/s*#', '/', trim ( $categoryPathStr ) );
That line replaces "s*/s*" with "/", which is why you have your last s removed. I see no real reason for that preg_replace (?), so just remove that line, or replace it with
$categoryPathStr = trim ( $categoryPathStr );
so that leading/ending white spaces get removed.

Categories