Alternatives to eval() - Shortcodes in text input - php

in a CMS I want the user to be able to include blocks of php code like a contact form, a search form.
Something like
<? include 'contact.inc.php' ?>
Now, Wordpress does this by using Shortcodes, ie
[contact-form] [search form]
How can I do this easily?
I have a limited number of includes
I could use
eval('?> ' . $database_query . ' <?php ');
and put
<? include('contact.inc.php') ; ?>
in the contents of $database_query
but eval() in this situation is dangerous
so how can I make a simple shortcode system ?

I would avoid eval all together if you can. The issue with eval, particularly with form inputs is that if exploited, you are giving an attacker access to your entire web server. I would suggest using some server-side script that is looking for explicit shortcodes. You can even use the Wordpress function that they use as a starting point. You can also use a preg_match to search for your shortcodes in the text and parse them that way. Either option would be better than eval()

Assuming you've loaded some HTML with your shortcodes from somewhere (database for example) and you want to echo it's contents with your forms inside, I would do the following:
// Define known shortcodes
$shortcodes = array(
"[contact-form]" => "contact.inc.php",
"[search-form]" => "search.inc.php"
);
// Load HTML with shortcodes from wherever you want
$source = load_template_from_somewhere();
// Now let's find the shortcodes in your source
$replace_points = array();
foreach($shortcodes as $code => $include_me){
$offset = 0;
while(TRUE){
$index = strpos($source, $code, $offset);
if($index === FALSE){
// No more shortcodes of this type found
break;
};
// Save the position and name of shortcode
$replace_points[$index] = $code;
// Update offset to scan the source search the remaining part of the string
$offset = $index + strlen($code);
};
};
// Sort the array because we've been searching by shortcode names
// And now we need to include forms in the correct order
ksort($replace_points);
$offset = 0;
foreach($replace_points as $index => $code){
// Echo raw HTML part of the string
echo substr($source, $offset, $index);
// Then include the form
include($shortcodes[$code]);
// Update the offset to move towards the end of the string
$offset = $index + strlen($code);
};
// Echo the remaining part of raw HTML string
echo substr($source, $offset);

Related

Replacing a single varible works, how do I replace every occurance of a tag?

So i made a basic template system for a project my company is developing. Currently I am able to load a template and print it out, and use a tag we developed to call templates within another template by calling <template::>template_name<::/template> in the master template. This works awesome as long as I only call ONE template. After the first occurance of the template tag, the system stops and doesn't go on to read the rest of the string from the database to get possible other template tags.
If i call the SAME template multiple times in the script, it will render right, but if I call a NEW template, it gets ignored by the system and gets printed as a normal text.
Here is my current functions for calling the tags and replacing them:
$inject_template = $this->get_template_name_between($template, "<template::>", "<::/template>");
if(!empty($inject_template)) {
$query_for_template = "SELECT * FROM ".$field." WHERE templateid = '".$inject_template."' LIMIT 1";
$injected_template_data = $wms->function->query($query_for_template);
while($returned = mysql_fetch_array($injected_template_data)) {
$type = $returned['templatetype'];
}
$template = str_replace('<template::>'.$inject_template.'<::/template>', $this->injectTemplate($inject_template, $type), $template);
}
public function injectTemplate($template_id, $template_number_type) {
return $this->extract_template($template_id, $template_number_type);
}
public function get_template_name_between($string, $start, $end) {
$ini = strpos($string, $start);
if($ini == 0) return '';
$ini += strlen($start);
$len = strpos($string, $end, $ini) - $ini;
return substr($string, $ini, $len);
}
Remember, this works if I call ONE tag, I simply need a way to force the function to replace ALL the tags with the right template for each. I was thinking maybe a for loop, and counting, but not sure the correct syntax to do that and I've been staring at this all day now lol. Thanks in advance!
You can use a loop that keeps replacing until there are no templates in the string.
while ($inject_template = $this->get_template_name_between($template, "<template::>", "<::/template>")) {
$query_for_template = "SELECT * FROM ".$field." WHERE templateid = '".$inject_template."' LIMIT 1";
$injected_template_data = $wms->function->query($query_for_template);
$returned = mysql_fetch_array($injected_template_data);
$type = $returned ? $returned['templatetype'] : '';
$template = str_replace('<template::>'.$inject_template.'<::/template>', $this->injectTemplate($inject_template, $type), $template);
}
You don't need to use a while() loop around mysql_fetch_array(), since the query is limited to 1 row.
BTW, you should stop using the mysql_* functions. Convert to mysqli or PDO, and use prepared queries.

PHP replace {replace_me} with <?php include ?> in output buffer

I have a file like this
**buffer.php**
ob_start();
<h1>Welcome</h1>
{replace_me_with_working_php_include}
<h2>I got a problem..</h2>
ob_end_flush();
Everything inside the buffer is dynamically made with data from the database.
And inserting php into the database is not an option.
The issue is, I got my output buffer and i want to replace '{replace}' with a working php include, which includes a file that also has some html/php.
So my actual question is: How do i replace a string with working php-code in a output-buffer?
I hope you can help, have used way to much time on this.
Best regards - user2453885
EDIT - 25/11/14
I know wordpress or joomla is using some similar functions, you can write {rate} in your post, and it replaces it with a rating system(some rate-plugin). This is the secret knowledge I desire.
You can use preg_replace_callback and let the callback include the file you want to include and return the output. Or you could replace the placeholders with textual includes, save that as a file and include that file (sort of compile the thing)
For simple text you could do explode (though it's probably not the most efficient for large blocks of text):
function StringSwap($text ="", $rootdir ="", $begin = "{", $end = "}") {
// Explode beginning
$go = explode($begin,$text);
// Loop through the array
if(is_array($go)) {
foreach($go as $value) {
// Split ends if available
$value = explode($end,$value);
// If there is an end, key 0 should be the replacement
if(count($value) > 1) {
// Check if the file exists based on your root
if(is_file($rootdir . $value[0])) {
// If it is a real file, mark it and remove it
$new[]['file'] = $rootdir . $value[0];
unset($value[0]);
}
// All others set as text
$new[]['txt'] = implode($value);
}
else
// If not an array, not a file, just assign as text
$new[]['txt'] = $value;
}
}
// Loop through new array and handle each block as text or include
foreach($new as $block) {
if(isset($block['txt'])) {
echo (is_array($block['txt']))? implode(" ",$block['txt']): $block['txt']." ";
}
elseif(isset($block['file'])) {
include_once($block['file']);
}
}
}
// To use, drop your text in here as a string
// You need to set a root directory so it can map properly
StringSwap($text);
I might be misunderstanding something here, but something simple like this might work?
<?php
# Main page (retrieved from the database or wherever into a variable - output buffer example shown)
ob_start();
<h1>Welcome</h1>
{replace_me_with_working_php_include}
<h2>I got a problem..</h2>
$main = ob_get_clean();
# Replacement
ob_start();
include 'whatever.php';
$replacement = ob_get_clean();
echo str_replace('{replace_me_with_working_php_include}', $replacement, $main);
You can also use a return statement from within an include file if you wish to remove the output buffer from that task too.
Good luck!
Ty all for some lovely input.
I will try and anwser my own question as clear as I can.
problem: I first thought that I wanted to implement a php-function or include inside a buffer. This however is not what I wanted, and is not intended.
Solution: Callback function with my desired content. By using the function preg_replace_callback(), I could find the text I wanted to replace in my buffer and then replace it with whatever the callback(function) would return.
The callback then included the necessary files/.classes and used the functions with written content in it.
Tell me if you did not understand, or want to elaborate/tell more about my solution.

Executing a PHP page after search + replacing keywords for language translation in the HTML

I am translating my website into different languages and I have over 130 pages so i want to pass my .php files through a function that will replace keywords
IE: Accessories = อุปกรณ์
Which is English to Thai.
I can get it to work using my method however... I have php (obviously) in these pages, and the output only displays the html and not executing the php
Is there a header method or something I have to pass at the start of my php pages..
here is the function I'm using to find text results and then replace them from my php files..
<?php
// lang.php
function get_lang($file)
{
// Include a language file
include 'lang_thai.php';
// Get the data from the HTML
$html = file_get_contents($file);
// Create an empty array for the language variables
$vars = array();
// Scroll through each variable
foreach($lang as $key => $value)
{
// Finds the array results in my lang_thai.php file (listed below)
$vars[$key] = $value;
}
// Finally convert the strings
$html = strtr($html, $vars);
// Return the data
echo $html;
}
?>
//This is the lang_thai.php file
<?php
$lang = array(
'Hot Items' => 'รายการสินค้า',
'Accessories' => 'อุปกรณ์'
);
?>
A lot of frameworks use a function to translate as it goes instead of replacing after the fact using .pot files. The function would look like this:
<h1><?php echo _('Hello, World') ?>!</h1>
So if it was English and not translated that function would just return the string untranslated. If it was to be translated then it would return the translated string.
If you want to continue with your route which is definitely faster to implement try this:
<?php
function translate($buffer) {
$translation = include ('lang_tai.php');
$keys = array_keys($translation);
$vals = array_values($translation);
return str_replace($keys, $vals, $buffer);
}
ob_start('translate');
// ... all of your html stuff
Your language file is:
<?php
return array(
'Hot Items' => 'รายการสินค้า',
'Accessories' => 'อุปกรณ์'
);
One cool thing is include can return values! So this is a good way to pass values from a file. Also the ob_start is an output buffer with a callback. So what happens is after you echo all of your html to the screen, right before it actually displays to the screen it passes all of that data to the translate function and we then translate all of the data!

Using preg_replace_callback to identify and manipulate latex code

I have latex + html code somewhere in the following form:
...some text1.... \[latex-code1\]....some text2....\[latex-code2\]....etc
Firstly I want to obtain the latex codes in an array codes[] to be able to send them to a server for rendering, so that
code[0]=latex-code1, code[1]=latex-code2, etc
Secondly, I want to modify this text so that it looks like:
...some text1.... <img src="root/1.png">....some text2....<img src="root/2.png">....etc
i.e, the i-th latex code fragment is replaced by the link to the i-th rendered image.
I have been trying to do this with preg_replace_callback and preg_match_all but being new to PHP haven't been able to make it work. Please advise.
If you're looking for codez:
$html = '...some text1.... \[latex-code1\]....some text2....\[latex-code2\]....etc';
$codes = array();
$count = 0;
$replace = function($matches) use (&$codes, &$count) {
list(, $codes[]) = $matches;
return sprintf('<img src="root/%d.png">', ++$count);
};
$changed = preg_replace_callback('~\\\\\\[(.+?)\\\\\\]~', $replace, $html);
echo "Original: $html\n";
echo "Changed : $changed\n\nLatex Codes: ", print_r($codes, 1), "Count: ", $count;
I don't know at which part you've got the problems, if it's the regex pattern, you use characters inside your markers that needs heavy escaping: For PHP and PCRE, that's why there are so many slashes.
Another tricky part is the callback function because it needs to collect the codes as well as having a counter. It's done in the example with an anonymous function that has variable aliases / references in it's use clause. This makes the variables $codes and $count available inside the callback.

Inserting multiple links into text, ignoring matches that happen to be inserted

The site I'm working on has a database table filled with glossary terms. I am building a function that will take some HTML and replace the first instances of the glossary terms with tooltip links.
I am running into a problem though. Since it's not just one replace, the function is replacing text that has been inserted in previous iterations, so the HTML is getting mucked up.
I guess the bottom line is, I need to ignore text if it:
Appears within the < and > of any HTML tag, or
Appears within the text of an <a></a> tag.
Here's what I have so far. I was hoping someone out there would have a clever solution.
function insertGlossaryLinks($html)
{
// Get glossary terms from database, once per request
static $terms;
if (is_null($terms)) {
$query = Doctrine_Query::create()
->select('gt.title, gt.alternate_spellings, gt.description')
->from('GlossaryTerm gt');
$glossaryTerms = $query->rows();
// Create whole list in $terms, including alternate spellings
$terms = array();
foreach ($glossaryTerms as $glossaryTerm) {
// Initialize with title
$term = array(
'wordsHtml' => array(
h(trim($glossaryTerm['title']))
),
'descriptionHtml' => h($glossaryTerm['description'])
);
// Add alternate spellings
foreach (explode(',', $glossaryTerm['alternate_spellings']) as $alternateSpelling) {
$alternateSpelling = h(trim($alternateSpelling));
if (empty($alternateSpelling)) {
continue;
}
$term['wordsHtml'][] = $alternateSpelling;
}
$terms[] = $term;
}
}
// Do replacements on this HTML
$newHtml = $html;
foreach ($terms as $term) {
$callback = create_function('$m', 'return \'<span>\'.$m[0].\'</span>\';');
$term['wordsHtmlPreg'] = array_map('preg_quote', $term['wordsHtml']);
$pattern = '/\b('.implode('|', $term['wordsHtmlPreg']).')\b/i';
$newHtml = preg_replace_callback($pattern, $callback, $newHtml, 1);
}
return $newHtml;
}
Using Regexes to process HTML is always risky business. You will spend a long time fiddling with the greediness and laziness of your Regexes to only capture text that is not in a tag, and not in a tag name itself. My recommendation would be to ditch the method you are currently using and parse your HTML with an HTML parser, like this one: http://simplehtmldom.sourceforge.net/. I have used it before and have recommended it to others. It is a much simpler way of dealing with complex HTML.
I ended up using preg_replace_callback to replace all existing links with placeholders. Then I inserted the new glossary term links. Then I put back the links that I had replaced.
It's working great!

Categories