Skip to content

Instantly share code, notes, and snippets.

@dukeofharen
Last active June 18, 2023 19:20
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save dukeofharen/e2c60b4478408b53d743 to your computer and use it in GitHub Desktop.
Save dukeofharen/e2c60b4478408b53d743 to your computer and use it in GitHub Desktop.
WordPress like shortcode parser for PHP
<?php
//The content which should be parsed
$content = '<p>Hello, my name is John an my age is [calc-age day="4" month="10" year="1991"].</p>';
$content .= '<p>Hello, my name is Carol an my age is [calc-age day="26" month="11" year="1996"].</p>';
//The array with all the shortcode handlers. This is just a regular associative array with anonymous functions as values. A very cool new feature in PHP, just like callbacks in JavaScript or delegates in C#.
$shortcodes = array(
"calc-age" => function($data){
$content = "";
//Calculate the age
if(isset($data["day"], $data["month"], $data["year"])){
$age = date("Y") - $data["year"];
if(date("m") < $data["month"]){
$age--;
}
if(date("m") == $data["month"] && date("d") < $data["day"]){
$age--;
}
$content = $age;
}
return $content;
}
);
//http://stackoverflow.com/questions/18196159/regex-extract-variables-from-shortcode
function handleShortcodes($content, $shortcodes){
//Loop through all shortcodes
foreach($shortcodes as $key => $function){
$dat = array();
preg_match_all("/\[".$key." (.+?)\]/", $content, $dat);
if(count($dat) > 0 && $dat[0] != array() && isset($dat[1])){
$i = 0;
$actual_string = $dat[0];
foreach($dat[1] as $temp){
$temp = explode(" ", $temp);
$params = array();
foreach ($temp as $d){
list($opt, $val) = explode("=", $d);
$params[$opt] = trim($val, '"');
}
$content = str_replace($actual_string[$i], $function($params), $content);
$i++;
}
}
}
return $content;
}
echo handleShortcodes($content, $shortcodes);
?>
@visiblegroup
Copy link

Hello,

Would this work with the below kind of shortcodes where they should basically be replaced with a 'div' tags of corresponding classes?

[la-row] [la-column width="100%"] [la-text format="h1"]Welcome![/la-text] [la-text]Sample text[/la-text] [/la-column] [/la-row] [la-row] [la-column width="25%"] [la-text format="h4"]Sample heading 3[/la-text] [la-text]Sample text[/la-text] [/la-column] [la-column width="25%"] [la-text format="h4"]Sample heading 3[/la-text] [la-text]Sample text[/la-text] [/la-column] [la-column width="25%"] [la-text format="h4"]Sample heading 3[/la-text] [la-text]Sample text[/la-text] [/la-column] [la-column width="25%"] [la-text format="h4"]Sample heading 3[/la-text] [la-text]Sample text[/la-text] [/la-column] [/la-row]

In the end the page should look similar to this:
example1

I should be able to build the html with php once the shortcodes are working, but I would just like to check if your shortcode parser works with these kinds of multiple-level repeating shortcodes resembling a typical div structure of a webpage.

Thanks!

@jsantari
Copy link

Nice little piece of code - works great with one exception. You can't create a parameter less shortcode for example. [show-list]. This won't work until you add a dummy parameter such as [show-list sc="1"]. Not a huge deal but would be nice. Took a quick look at it but wasn't sure how to make that work.

@mateoprifti
Copy link

Nice code! How would this work though with spaces in attribute values such as [data att="Some Attribute"] ?

@mateoprifti
Copy link

Did some modification and got it working with this
`foreach($shortcodes as $key => $function){
$dat = array();
preg_match_all("/[".$key." (.+?)]/", $content, $dat);
if(count($dat) > 0 && $dat[0] != array() && isset($dat[1])){
$i = 0;
$actual_string = $dat[0];

            foreach($dat[1] as $temp){

                $atts    = array();
                $pattern = '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|\'([^\']*)\'(?:\s|$)|(\S+)(?:\s|$)/';
                $text    = preg_replace( "/[\x{00a0}\x{200b}]+/u", ' ', $temp );
                if ( preg_match_all( $pattern, $text, $match, PREG_SET_ORDER ) ) {
                    foreach ( $match as $m ) {
                        if ( ! empty( $m[1] ) ) {
                            $atts[ strtolower( $m[1] ) ] = stripcslashes( $m[2] );
                        } elseif ( ! empty( $m[3] ) ) {
                            $atts[ strtolower( $m[3] ) ] = stripcslashes( $m[4] );
                        } elseif ( ! empty( $m[5] ) ) {
                            $atts[ strtolower( $m[5] ) ] = stripcslashes( $m[6] );
                        } elseif ( isset( $m[7] ) && strlen( $m[7] ) ) {
                            $atts[] = stripcslashes( $m[7] );
                        } elseif ( isset( $m[8] ) && strlen( $m[8] ) ) {
                            $atts[] = stripcslashes( $m[8] );
                        } elseif ( isset( $m[9] ) ) {
                            $atts[] = stripcslashes( $m[9] );
                        }
                    }

                    // Reject any unclosed HTML elements.
                    foreach ( $atts as &$value ) {
                        if ( false !== strpos( $value, '<' ) ) {
                            if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) {
                                $value = '';
                            }
                        }
                    }
                } else {
                    $atts = ltrim( $text );
                }

                $temp = $atts;

                $params = array();
                foreach ($temp as $key => $d){
                    $params[$key] = trim($d, '"');
                }
                $content = str_replace($actual_string[$i], $function($params), $content);
                $i++;
            }
        }
    }`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment