PHP - simple shortcode parser, output order is wrong - php

I have a simple function which parse shortcode tags and its attribute,
but it has some problem in output.
Like, this is my content in a string with a shortcode inside it:
$content = 'This is lorem ispium test [gallery image="10"] and text continues...'
I want the result output like this:
This is lorem ispium test
----------------------------------------------
| This is output of gallery |
-----------------------------------------------
and text continues...
But now shortcode is not rendering where the shortcode is called, instead of this shortcode render at the top. like:
----------------------------------------------
| This is output of gallery |
-----------------------------------------------
This is lorem ispium test and text continues...
Kindly tell how do I render shortcode where it was called
function shortcode($content) {
$shortcodes = implode('|', array_map('preg_quote', get('shortcodes')));
$pattern = "/(.?)\[($shortcodes)(.*?)(\/)?\](?(4)|(?:(.+?)\[\/\s*\\2\s*\]))?(.?)/s";
echo preg_replace_callback($pattern, array($this,'handleShortcode'), $content);
}
function handleShortcode($matches) {
$prefix = $matches[1];
$suffix = $matches[6];
$shortcode = .$matches[2];
// allow for escaping shortcodes by enclosing them in double brackets ([[shortcode]])
if($prefix == '[' && $suffix == ']') {
return substr($matches[0], 1, -1);
}
$attributes = array(); // Parse attributes into into this array.
if(preg_match_all('/(\w+) *= *(?:([\'"])(.*?)\\2|([^ "\'>]+))/', $matches[3], $match, PREG_SET_ORDER)) {
foreach($match as $attribute) {
if(!empty($attribute[4])) {
$attributes[strtolower($attribute[1])] = $attribute[4];
} elseif(!empty($attribute[3])) {
$attributes[strtolower($attribute[1])] = $attribute[3];
}
}
}
//callback to gallery
return $prefix. call_user_func(array($this,$shortcode), $attributes, $matches[5], $shortcode) . $suffix;
}
function gallery($att, $cont){
//gallery output
}
Please note: it is not related to wordpress, it is a custom script.

I believe that the problem may be in your function gallery($att, $cont).
If that function uses echo or print instead of return, then it makes perfect sense to show up before the actual content does.
EDIT:
If you can't change the gallery code, then yes, you can use output buffering.
function handleShortcode($matches) {
...
ob_start();
call_user_func(array($this,$shortcode), $attributes, $matches[5], $shortcode);
$gallery_output = ob_get_contents();
ob_end_clean();
return $prefix . $gallery_output . $suffix;
}
Related readings:
PHP ob_start
PHP ob_get_contents

Related

Replace markdown with html in wordpress

I am trying to replace all the wiki markdown text from my custom post. Example:
== My Subheading ==
=== My sub Subheading ===
==== another heading ====
I am trying to change that content like below:
My Subheading My sub Subheading another
heading
So, I tried to use below function. But, didn't worked!
I am seeing:
Parse error: syntax error, unexpected token "{", expecting "("
I am not much familiar with WP custom function. Can u guys please help me?
function the_content{
private $patterns, $replacements;
public function __construct($analyze=false) {
$this->patterns=array(
"/\r\n/",
// Headings
"/^==== (.+?) ====$/m",
"/^=== (.+?) ===$/m",
"/^== (.+?) ==$/m",
// Formatting
"/\'\'\'\'\'(.+?)\'\'\'\'\'/s",
"/\'\'\'(.+?)\'\'\'/s",
"/\'\'(.+?)\'\'/s",
// Special
"/^----+(\s*)$/m",
"/\[\[(file|img):((ht|f)tp(s?):\/\/(.+?))( (.+))*\]\]/i",
"/\[((news|(ht|f)tp(s?)|irc):\/\/(.+?))( (.+))\]/i",
"/\[((news|(ht|f)tp(s?)|irc):\/\/(.+?))\]/i",
// Indentations
"/[\n\r]: *.+([\n\r]:+.+)*/",
"/^:(?!:) *(.+)$/m",
"/([\n\r]:: *.+)+/",
"/^:: *(.+)$/m",
// Ordered list
"/[\n\r]?#.+([\n|\r]#.+)+/",
"/[\n\r]#(?!#) *(.+)(([\n\r]#{2,}.+)+)/",
// Unordered list
"/[\n\r]?\*.+([\n|\r]\*.+)+/",
"/[\n\r]\*(?!\*) *(.+)(([\n\r]\*{2,}.+)+)/",
// List items
"/^[#\*]+ *(.+)$/m",
"/^(?!<li|dd).+(?=(<a|strong|em|img)).+$/mi",
"/^[^><\n\r]+$/m",
);
$this->replacements=array(
"\n",
// Headings
"<h3>$1</h3>",
"<h2>$1</h2>",
"<h1>$1</h1>",
//Formatting
"<strong><em>$1</em></strong>",
"<strong>$1</strong>",
"<em>$1</em>",
// Special
"<hr/>",
"<img src=\"$2\" alt=\"$6\"/>",
"$7",
"$1",
// Indentations
"\n<dl>$0\n</dl>",
"<dd>$1</dd>",
"\n<dd><dl>$0\n</dl></dd>",
"<dd>$1</dd>",
// Ordered list
"\n<ol>\n$0\n</ol>",
"\n<li>$1\n<ol>$2\n</ol>\n</li>",
// Unordered list
"\n<ul>\n$0\n</ul>",
"\n<li>$1\n<ul>$2\n</ul>\n</li>",
// List items
"<li>$1</li>",
// Newlines
"$0<br/>",
"$0<br/>",
);
if($analyze) {
foreach($this->patterns as $k=>$v) {
$this->patterns[$k].="S";
}
}
}
public function parse($input) {
if(!empty($input))
$output=preg_replace($this->patterns,$this->replacements,$input);
else
$output=false;
return $output;
}
}
Mainly I am trying to use a filter on the_content, which will convert markdown text to simple HTML using regex replace.
There were a few issues.
You weren't declaring your class properly (it is not a "function").
There was trailing space after == My Subheading == (before the end of line marker -- you need to allow zero or more spaces before $)
Please educate yourself on PHP's PSR-12 coding standards -- this will help you to write clean, consistent, and professional code.
Code: (Demo)
$text = <<<TEXT
== My Subheading ==
=== My sub Subheading ===
==== another heading ====
TEXT;
class ContentParser
{
private $patterns = [];
private $replacements = [];
public function __construct() {
$this->patterns = [
// Headings
"/^==== (.+?) ====\h*$/m",
"/^=== (.+?) ===\h*$/m",
"/^== (.+?) ==\h*$/m",
];
$this->replacements = [
// Headings
"<h3>$1</h3>",
"<h2>$1</h2>",
"<h1>$1</h1>",
];
}
public function parse($input) {
if (!empty($input)) {
$output = preg_replace($this->patterns, $this->replacements, $input);
} else {
$output = false; // I don't recommend returning a boolean when otherwise returning strings
}
return $output;
}
}
$object = new ContentParser();
var_export($object->parse($text));
Output: (the single quotes are from var_export(), you can use echo instead)
'<h1>My Subheading</h1>
<h2>My sub Subheading</h2>
<h3>another heading</h3>'

How to use str_replace in PHP so it doesn't effect html tags and attributes

I want to change some words (random word leaving first and last word) in page in Wordpress . For example Team will be Taem, Blame will be Bamle. I am using str_replace to acheive this with the_content filter
function replace_text_wps($text){
$textr=wp_filter_nohtml_kses( $text );
$rtext= (explode(" ",$textr));
$rep=array();
foreach($rtext as $r)
{
//echo $r;
if (strlen($r)>3)
{
if(ctype_alpha($r)){
$first=substr($r,0,1);
$last=substr($r,-1);
$middle=substr($r,1,-1);
$rep[$r]=$first.str_shuffle($middle).$last;
}
}
}
$text = str_replace(array_keys($rep), $rep, $text);
return $text;
}
add_filter('the_content', 'replace_text_wps',99);
The issue I am facing is when I run str_replace it also changes text in links and classes of HTML. I just want to change the text not html.
For example if I change Content word
<a class='elementor content'>Content Here</a> It becomes <a class='elementor conentt'>Conentt Here</a
Can someone provide a Good solution for this?
If you realy have to use str_replaceā€¦
Use preg_split to split between HTML tags and plain text:
function my_text_filter($text) {
$out = "";
$parts = preg_split('/(<[^>]+>)/', $text, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach ($parts as $part) {
if ($part && '<' === $part[0] && '>' === substr($part, -1)) {
$out .= $part; // Is a HTML tag, skip!
continue;
}
$out .= replace_text_wps($part);
}
return $out;
}
add_filter('the_content', 'my_text_filter', 99);

limit_text is only outputting plain text and not showing bold font

I have some code that outputs a title and a description for a list of posts. I am trying to limit the length of text for both. The title is $story->title, and outputs just fine. However the description, represented by $story->excerpt in the code, contains html tags in the database. The limit_text function seems to strip these tags from the text. I think I need to limit the characters differently or need a function to allow those tags to work.
I have tried some functions that allows the tags to be seen but not function properly. But I am new to php in general so I don't know many functions.
<?php
foreach($stories as $story) {
echo '<h2>'.limit_text($story->title, 80).'</h2>';
if(!empty($story->excerpt)) {
echo '<p>'.limit_text($story->excerpt, 150).'</p>';
} else {
echo limit_text($story->body, 150);
}
}
?>
I found the function for limit_text
function limit_text($string, $limit = 140) {
$string = preg_replace('/<figcaption>.*?<\/figcaption>/','',$string);
$string = preg_replace('/<div class=\"wp_image_caption\">.*?<\/div>/','',$string);
$string = str_replace(' ','',$string);
$string = substr($string, strpos($string, "<p><strong>"));
$string = strip_tags($string);
$string = substr($string, 0, $limit);
$string = $string.'...';
return $string;
}
The reason why my tags were being stripped was the strip_tagsmethod right in the function. Duh. Hehe. Thanks for the comments.
Try this limit text function instead
public function limitText($text, $len)
{
$text = preg_match('#<\s*?b\b[^>]*>(.*?)</b\b[^>]*>#s', $text, $matches);
return "<b>" . substr($text,0, $len) . "</b>";
}

function to scrape page keywords , description and title?

i wrote simple 3 functions to scrape titles , description and keywords of simple html page
this is the first function to scrape titles
function getPageTitle ($url)
{
$content = $url;
if (eregi("<title>(.*)</title>", $content, $array)) {
$title = $array[1];
return $title;
}
}
and it works fine
and those are 2 functions to scrape description and keywords and those not working
function getPageKeywords($url)
{
$content = $url;
if ( preg_match('/<meta[\s]+[^>]*?name[\s]?=[\s\"\']+keywords[\s\"\']+content[\s]?=[\s\"\']+(.*?)[\"\']+.*?>/i', $content, $array)) {
$keywords = $array[1];
return $keywords;
}
}
function getPageDesc($url)
{
$content = $url;
if ( preg_match('/<meta[\s]+[^>]*?name[\s]?=[\s\"\']+description[\s\"\']+content[\s]?=[\s\"\']+(.*?)[\"\']+.*?>/i', $content, $array)) {
$desc = $array[1];
return $desc;
}
}
i know there may be something wrong with the preg_match line but i really don't know
i tried it so much things but it doesn't work
Why not use get_meta_tags? PHP Documentation Here
<?php
// Assuming the above tags are at www.example.com
$tags = get_meta_tags('http://www.example.com/');
// Notice how the keys are all lowercase now, and
// how . was replaced by _ in the key.
echo $tags['author']; // name
echo $tags['keywords']; // php documentation
echo $tags['description']; // a php manual
echo $tags['geo_position']; // 49.33;-86.59
?>
NOTE You can change the parameter to either a URL, local file or string.
Its better to use php's native DOMDocument to parse HTML then regex, you can also use , tho in this day in age allot of sites dont even add the keywords, description tags no more, so you cant rely on them always being there. But here is how you can do it with DOMDocument:
<?php
$source = file_get_contents('http://php.net');
$dom = new DOMDocument("1.0","UTF-8");
#$dom->loadHTML($source);
$dom->preserveWhiteSpace = false;
//Get Title
$title = $dom->getElementsByTagName('title')->item(0)->nodeValue;
$description = '';
$keywords = '';
foreach($dom->getElementsByTagName('meta') as $metas) {
if($metas->getAttribute('name') =='description'){ $description = $metas->getAttribute('content'); }
if($metas->getAttribute('name') =='keywords'){ $keywords = $metas->getAttribute('content'); }
}
print_r($title);
print_r($description);
print_r($keywords);
?>

Highlight text, except html tags

I'm using the code below to highlight some keywords in a text:
$message = str_ireplace($words,'<span class="hightlighted_text">'.$words.'</span>',$message);
The text may contain some html tags, for example <img>, <strong>, etc..
How can I highlight "normal" text, except the text between the html tags? Because when users search for "img" the <img> text will be highlighted and the image doesn't work anymore.
Use a DOM parser of some sort. This is not something you want to do with regex.
From http://forum.phpfrance.com/vos-contributions/remplacement-selectif-hors-dans-balises-html-t199.html
function mon_rplc_callback($capture){
global $arg;
return ($arg['flag'] == 1)
? $arg['fct']($arg['from'], $arg['to'], $capture[1]).$capture[2]
: $capture[1].$arg['fct']($arg['from'], $arg['to'], $capture[2]);
}
function split_tag($from, $to, $txt, $fct, $flag = 1){
global $arg;
$arg = compact('from', 'to', 'fct', 'flag');
return preg_replace_callback('#((?:(?!<[/a-z]).)*)([^>]*>|$)#si', "mon_rplc_callback", $txt);
}
When $flag == 1, the replacement function is applied outside HTML.
When $flag == -1, the replacement function is applied inside HTML.
Applied to your example, it would give something like this:
echo split_tag($words, '<span class="hightlighted_text">'.$words.'</span>', $message, 'str_ireplace', 1);
Enjoy! ;)
Better code based on reply from #Savageman
$str = 'ba';
$highlightWhat = "ba";
$str = preg_replace_callback('#((?:(?!<[/a-z]).)*)([^>]*>|$)#si', function($m) use ($highlightWhat) {
return preg_replace('~('.$highlightWhat.')~i', '<span style="background:#fff330">$1</span>', $m[1]) . $m[2];
},
$str);

Categories