Redirect on disallowed characters - php

I looked over and over and I couldn't seem to find an answer to what I want, but here goes:
I have a client that is really computer illiterate and I want to ensure that any errors that arise are handled without intervention. The one thing I'm stuck on currently though is the "The URI you submitted has disallowed characters." error.
I don't want to simply allow all the characters to circumvent the error. Instead what I'd like to do is simply redirect to a particular URI when this error happens. For example:
www.local.com/project/login/'''' ---> www.local.com/project/login
I looked at doing it with hooks, but I'd like to be able to specify a custom URL when I want to execute the check.
I was thinking about using a call to a library method which will pull in the characters from the configuration, then redirect based on whether the check passes or not, with the current URL and URI redirect as a parameter. Is this the way to do it, or is there an easier way to manage this?

I've read all the comments above - but I think you missed the easy way to do this.
Just overload the _filter_uri() function, and do whatever you want:
(Place this file in application/core/MY_URI.php)
// Normally this is not fully uppercase - but for some reason the URI filename is
Class MY_URI extends CI_URI
{
/**
* Filter segments for malicious characters
*
* #access private
* #param string
* #return string
*/
function _filter_uri($str)
{
if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
{
if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
{
// DO SOMETHING HERE LIKE REDIRECT OR CHANGE THE URL
}
}
// Convert programatic characters to entities
$bad = array('$', '(', ')', '%28', '%29');
$good = array('$', '(', ')', '(', ')');
return str_replace($bad, $good, $str);
}

This is solution I am using on my project:
File: application/core/MY_URI.php
class MY_URI extends CI_URI {
/**
* Filter URI
*
* Filters segments for malicious characters.
*
* #param string $str
* #return void
*/
public function filter_uri(&$str)
{
if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $str))
{
return preg_replace('~[^a-zA-Z 0-9%.:_\-,()]+~', '', $str);
}
}
}

Related

Codeigniter encryption without slash

I know this may look like duplicate from this question: Ignore slash while using encryption in Codeigniter. But I still didn't have the answer from it.
I want to sent encrypted email name as URL to their email account.
Then that URL is decrypted to search if that email name is exist in my database to permit that email into my system.
The problem is:
If I use urlencode or base64_encode after encryption, it always resulted in empty value to search the database after decrypt. I think it because the encrypted value always changing.
If I use the casual encryption, it might have the ("/") character.
If I only use the encode, without the encryption, it might permit the email name to have access into my system.
Lastly, I found some library: Ignore Slash while using encryption in codeigniter - GitHub .
But it gave me this error: Undefined property: CI_Loader::$my_encrypt
I don't know what I've done wrong, I already:
Capitalized the class name first letter.
Using the same file name with the class name. (capitalized too)
Change the extend to CI_Encryption because the Encrypt class is already deprecated.
Insert the public function __construct() {parent::__construct();} before all method.
Place the file inside application/library.
Load the library $this->load->library('my_encrypt');
Load the method using $this->my_encrypt->encode($key); this is the line that gave me an error.
I know that this may sound like a simple mistake, but I'm using another third-party library too but it didn't give me an error at all.
Can anyone help me find the mistake / missing step there?
Update -
Before I load the library in the controller, I want to check the result first in view. But it doesn't give me any changes even when I put the code inside controller. Here is the code :
$key = 'example#gmail.com';
$this->load->library('my_encrypt');
$segment = $this->my_encrypt->encode($key);
echo $segment;
echo ( $this->my_encrypt->decode($segment) );
Update:
Fix library code to extend with CI_Encryption library
Have you loaded the library? Name librabry as MY_Encrypt.php in application libraries
<?php
class MY_Encrypt extends CI_Encrypt
{
/**
* Encodes a string.
*
* #param string $string The string to encrypt.
* #param string $key[optional] The key to encrypt with.
* #param bool $url_safe[optional] Specifies whether or not the
* returned string should be url-safe.
* #return string
*/
public function __construct() {
parent::__construct();
}
function encode($string, $key="", $url_safe=TRUE)
{
$ret = parent::encode($string, $key);
if ($url_safe)
{
$ret = strtr(
$ret,
array(
'+' => '.',
'=' => '-',
'/' => '~'
)
);
}
return $ret;
}
/**
* Decodes the given string.
*
* #access public
* #param string $string The encrypted string to decrypt.
* #param string $key[optional] The key to use for decryption.
* #return string
*/
function decode($string, $key="")
{
$string = strtr(
$string,
array(
'.' => '+',
'-' => '=',
'~' => '/'
)
);
return parent::decode($string, $key);
}
}
?>
Now call the encrypt library and use the encryption class instead of my_encrypt
$key='Welcome';
$this->load->library('encrypt');
$key1= $this->encrypt->encode($key);
echo $key1;
fixed to extend the CI_Encryption library, sorry for bothering. :)
class MY_Encrypt extends CI_Encryption
{
/**
* Encodes a string.
*
* #param string $string The string to encrypt.
* #param string $key[optional] The key to encrypt with.
* #param bool $url_safe[optional] Specifies whether or not the
* returned string should be url-safe.
* #return string
*/
public function __construct() {
parent::__construct();
}
function encode($string)
{
$ret = parent::encrypt($string);
if ( !empty($string) )
{
$ret = strtr(
$ret,
array(
'+' => '.',
'=' => '-',
'/' => '~'
)
);
}
return $ret;
}
/**
* Decodes the given string.
*
* #access public
* #param string $string The encrypted string to decrypt.
* #param string $key[optional] The key to use for decryption.
* #return string
*/
function decode($string)
{
$string = strtr(
$string,
array(
'.' => '+',
'-' => '=',
'~' => '/'
)
);
return parent::decrypt($string);
}
}
?>

codeigniter url encrypt not working

<a href="<?php echo base_url().'daily_report/index/'.$this->encrypt->encode($this->session->userdata('employee_id')) ?>">
i have encrypted the above url using the codeigniter encrypt
i set the encryption key in codeigniter config file
$config['encryption_key'] = 'gIoueTFDwGzbL2Bje9Bx5B0rlsD0gKDV';
and i called in the autoload
$autoload['libraries'] = array('session','form_validation','encrypt','encryption','database');
when the ulr(href) load into the url it look like this
http://localhost/hrms/daily_report/index/FVjGcz4qQztqAk0jaomJiAFBZ/vKVSBug1iGPQeKQCZ/K7+WUE4E/M9u1EjWh3uKTKeIhExjGKK1dJ2awL0+zQ==
but the url is not decoded, and i;m not getting the employee_id it shows empty.
public function index($employee_id) {
$save_employee_id = $employee_id;
// decoding the encrypted employee id
$get_employee_id = $this->encrypt->decode($save_employee_id);
echo $employee_id; // answer: FVjGcz4qQztqAk0jaomJiAFBZ
echo "<br>";
echo $get_employee_id; // is display the null
echo "<br>";
exit();
// get the employee daily report
$data['get_ind_report'] = $this->daily_report_model->get_ind_report($get_employee_id);
// daily report page
$data['header'] = "Daily Report";
$data['sub_header'] = "All";
$data['main_content'] = "daily_report/list";
$this->load->view('employeelayout/main',$data);
}
complete url(3) is
FVjGcz4qQztqAk0jaomJiAFBZ/vKVSBug1iGPQeKQCZ/K7+WUE4E/M9u1EjWh3uKTKeIhExjGKK1dJ2awL0+zQ==
it shows only
FVjGcz4qQztqAk0jaomJiAFBZ
i tried to change in the
$config['permitted_uri_chars'] = 'a-zA-Z 0-9~%.:_\-#=+';
by / in the permitted uri chars
but it throwing error
So, i need to encryption the $id in the url using the codeigniter encrypt class and decrypt in the server side to get the actual $id, So that i fetch data from the DB. any help would be appreciated
You have to extend encryption class and avoid the / to get it working. Place this class in your application/libraries folder. and name it as MY_Encrypt.php.
class MY_Encrypt extends CI_Encrypt
{
/**
* Encodes a string.
*
* #param string $string The string to encrypt.
* #param string $key[optional] The key to encrypt with.
* #param bool $url_safe[optional] Specifies whether or not the
* returned string should be url-safe.
* #return string
*/
function encode($string, $key="", $url_safe=TRUE)
{
$ret = parent::encode($string, $key);
if ($url_safe)
{
$ret = strtr(
$ret,
array(
'+' => '.',
'=' => '-',
'/' => '~'
)
);
}
return $ret;
}
/**
* Decodes the given string.
*
* #access public
* #param string $string The encrypted string to decrypt.
* #param string $key[optional] The key to use for decryption.
* #return string
*/
function decode($string, $key="")
{
$string = strtr(
$string,
array(
'.' => '+',
'-' => '=',
'~' => '/'
)
);
return parent::decode($string, $key);
}
}
FVjGcz4qQztqAk0jaomJiAFBZ/vKVSBug1iGPQeKQCZ/K7+WUE4E/M9u1EjWh3uKTKeIhExjGKK1dJ2awL0+zQ==
Shows
FVjGcz4qQztqAk0jaomJiAFBZ
If you look at your url closely, you could see that after the result which has been shown there is a '/' . Now any string after that will be treated as another segment. Hence it could not decode.
The encrypt library in this case would not work.
Either you stop passing that through the URL or use another different technique base_encode().
Hope that helps
This is happening as the character "/" is part of html uri delimiter. Instead you can work around it by avoiding that character in html url by rawurlencoding your encrytion output string before attaching it to url.
\edit:
I tried rawurlencode, but wasn't able to get the proper output.
Finally succeeded by using this code.
Define two functions:
function hex2str( $hex ) {
return pack('H*', $hex);
}
function str2hex( $str ) {
return array_shift( unpack('H*', $str) );
}
Then use call str2hex and pass it the encrypted user id to convert encrypted string into hexcode.
Reverse the process to get the correct string so that you can decrypt it.
I was able to properly encode and decode:
"FVjGcz4qQztqAk0jaomJiAFBZ/vKVSBug1iGPQeKQCZ/K7+WUE4E/M9u1EjWh3uKTKeIhExjGKK1dJ2awL0+zQ=="
to:
"46566a47637a3471517a7471416b306a616f6d4a694146425a2f764b56534275673169475051654b51435a2f4b372b57554534452f4d397531456a576833754b544b65496845786a474b4b31644a3261774c302b7a513d3d"
The url would become rather long though.

Get filename with extension from url on log string using PHP PCRE regex

I'm writing a script for parsing a log file from an network device. The log file generated from the device it's not regular, the lines doesn't follow a logic sequence and haves multiple patterns. My script needs to extract from the log lines only the ones that matches an specific pattern and from that lines specific information as datetime, entry type, resource type and resource name from the url in the string. The pattern that I need to match it's the following:
dd-mm-yyyy hh:mm:ss INFO spx.resource.media - New Resource 'URI' [flags] (dlc/tcd)
where 'INFO' is the entry type, 'spx.resource.media' the resource type and in the URI resides the resource name. Currently we need to filter those that haves a specifics extensions.
I reviewed several posts that cover this subject and using this online tool: I came with this regular expresion:
/(\d{2}-\d{2}-\d{4}\s{1}\d{2}:\d{2}:\d{2})\s{1,}(\w{4})\s{1,}(spx.resource.media)(.{1,}(?<=(?:.jpg)|(?:.png)))/g
The problem is that the last regex group matches the whole URI plus the characters and spaces from the resource type and on, and y only need the filename with the extension. I tried this 'regex-to-get-a-filename-from-a-url' (can't post the link insufficient reputation) but doesn't workout 'cause the debugger marks the ^/ as unescaped delimiter. Also if removed doesn't work. A portion of the log can be found here. I really need to get this.
Thanks for reading and/or answering
have a look at this. First Identify the location of the file then you can loop through accordingly to get what you want
<?php
$handle = #fopen("/tmp/inputfile.txt", "r");
if ($handle) {
while (($buffer = fgets($handle, 4096)) !== false) {
echo $buffer;
}
if (!feof($handle)) {
echo "Error: unexpected fgets() fail\n";
}
fclose($handle);
}
?>
A month ago a came with a solution. What I wanted was to extract the filename and the rest of the subgroups with one pattern, I don´t know if this is possible but with my current regex skills is not. So what I did was to use three regex patterns as you can see in the code below:
This code is part of a class that I (obviously) called Parser. First I define the patterns as constants in the class.
/**
* #const string Log line pattern
*/
const LINE_REGEX_PATTERN = '/(\d{2}-\d{2}-\d{4}\s{1}\d{2}:\d{2}:\d{2})\s{1,}(\w{4})\s{1,}(spx.resource.media)(.{1,}(?<=%extensions%))/';
/**
* #const string Full URL pattern
*/
const FULL_URL_PATTERN = '/\b((?:https?|ftps?|file|spx):\/\/[-A-Z0-9+&##\/%?=~_|$!:,.;]*[A-Z0-9+&##\/%=~_|$])/i';
/**
* #const string Filename pattern
*/
const RESOURCE_REGEX_PATTERN = '/((?:[^\/][\d\w\.-]+)(?<=%extensions%))/';
As you can see, I use a placeholder for the file extensions because in this case I need them to be dynamically set by configuration or database query. Next I validate each extracted line against the first pattern
/**
* Line extract
*
* #param string $file_line File line string
*
* #return array An array if matches
* Array (
* [0] => Matched line
* [1] => Date\Time subgroup (format >> d-M-y H:i:s)
* [2] => String flag subgroup
* [3] => Resource type subgroup (not used)
* [4] => Text string containing resource URL
* )
* , null otherwise
*
* #throws RegexException If malformed pattern
*/
private function extractMatches($file_line)
{
$extensions = array();
// build valid extensions subgroup
foreach ($this->valid_extensions as $extension) {
$extensions[] = sprintf("(?:\.%s)", $extension);
}
$matches = array();
// replace extensions placeholder
$pattern = str_replace('%extensions%', implode('|', $extensions), self::LINE_REGEX_PATTERN);
$is_valid = preg_match($pattern, $file_line, $matches);
if ($is_valid === false) {
throw new RegexException();
}
return $matches;
}
From the resulting array (if any) I fetch the 5th element (the one that stores the text with the URL in it), then I passed to two other functions, the first one to full URL extraction and the second one to finally extract the filename. See below:
/**
* Full URL extract
*
* #param string $text Text with URL in it
*
* #return string The URL, empty string otherwise
*
* #throws RegexException If malformed pattern
*/
private function extractUrl($text)
{
$match = array();
$is_valid = preg_match(self::FULL_URL_PATTERN, $text, $match);
if ($is_valid === false) {
throw new RegexException();
} elseif ($is_valid === 1) {
return $match[0];
}
return ''; // No URL found!
}
/**
* Filename extract
*
* #param string $url Resource URL (expects no GET parameters)
*
* #return string Resource filename (includes extension), empty string otherwise
*
* #throws RegexException If malformed pattern
*/
private function extractResourceNameFromUrl($url)
{
$extensions = array();
// build valid extensions subgroup
foreach ($this->valid_extensions as $extension) {
$extensions[] = sprintf("(?:\.%s)", $extension);
}
$matches = array();
// replace extensions placeholder
$pattern = str_replace('%extensions%', implode('|', $extensions), self::RESOURCE_REGEX_PATTERN);
$is_valid = preg_match($pattern, $url, $matches);
if ($is_valid === false) {
throw new RegexException();
} elseif ($is_valid === 1) {
return $matches[1];
}
return '';
}
Finally some where in my app I just did:
$parser = new Parser();
// fetch file line loop
$matches = $parser->extractMatches($file_line);
$url = $parser->extractUrl($matches[4]);
$filename = $parser->extractResourceNameFromUrl($matches[4]);
Hope helps somebody. Thanks!

Kohana 3.2. - How can I use hyphens in URIs

Recently I've been doing some research into SEO and how URIs that use hyphens or underscores are treated differently, particularly by Google who view hyphens as separators.
Anyway, eager to adapt my current project to meet this criteria I found that because Kohana uses function names to define pages I was receiving the unexpected '-' warning.
I was wondering whether there was any way to enable the use of URIs in Kohana like:
http://www.mysite.com/controller/function-name
Obviously I could setup a routeHandler for this... but if I was to have user generated content, i.e. news. I'd then have to get all articles from the database, produce the URI, and then do the routing for each one.
Are there any alternative solutions?
Note: This is the same approach as in Laurent's answer, just slightly more OOP-wise. Kohana allows one to very easily overload any system class, so we can use it to save us some typing and also to allow for cleaner updates in the future.
We can plug-in into the request flow in Kohana and fix the dashes in the action part of the URL. To do it we will override Request_Client_Internal system class and it's execute_request() method. There we'll check if request->action has dashes, and if so we'll switch them to underscores to allow php to call our method properly.
Step 1. Open your application/bootstrap.php and add this line:
define('URL_WITH_DASHES_ONLY', TRUE);
You use this constant to quickly disable this feature on some requests, if you need underscores in the url.
Step 2. Create a new php file in: application/classes/request/client/internal.php and paste this code:
<?php defined('SYSPATH') or die('No direct script access.');
class Request_Client_Internal extends Kohana_Request_Client_Internal {
/**
* We override this method to allow for dashes in the action part of the url
* (See Kohana_Request_Client_Internal::execute_request() for the details)
*
* #param Request $request
* #return Response
*/
public function execute_request(Request $request)
{
// Check the setting for dashes (the one set in bootstrap.php)
if (defined('URL_WITH_DASHES_ONLY') and URL_WITH_DASHES_ONLY == TRUE)
{
// Block URLs with underscore in the action to avoid duplicated content
if (strpos($request->action(), '_') !== false)
{
throw new HTTP_Exception_404('The requested URL :uri was not found on this server.', array(':uri' => $request->uri()));
}
// Modify action part of the request: transform all dashes to underscores
$request->action( strtr($request->action(), '-', '_') );
}
// We are done, let the parent method do the heavy lifting
return parent::execute_request($request);
}
} // end_class Request_Client_Internal
What this does is simply replacing all the dashes in the $request->action with underscores, thus if url was /something/foo-bar, Kohana will now happily route it to our action_foo_bar() method.
In the same time we block all the actions with underscores, to avoid the duplicated content problems.
No way to directly map a hyphenated string to a PHP function so you will have to do routing.
As far as user generated content, you could do something like Stack Exchange does. Each time user content is saved to the database, generated a slug for it (kohana-3-2-how-can-i-use-hyphens-in-uris) and save it along with the other information. Then when you need to link to it, use the unique id and append the slug to the end (ex:http://stackoverflow.com/questions/7404646/kohana-3-2-how-can-i-use-hyphens-in-uris) for readability.
You can do this with lambda functions: http://forum.kohanaframework.org/discussion/comment/62581#Comment_62581
You could do something like
Route::set('route', '<controller>/<identifier>', array(
'identifier' => '[a-zA-Z\-]*'
))
->defaults(array(
'controller' => 'Controller',
'action' => 'show',
));
Then receive your content identifier in the function with Request::current()->param('identifier') and parse it manually to find the relating data.
After having tried various solutions, I found that the easiest and most reliable way is to override Kohana_Request_Client_Internal::execute_request. To do so, add a file in your application folder in "application\classes\kohana\request\client\internal.php" then set its content to:
<?php defined('SYSPATH') or die('No direct script access.');
class Kohana_Request_Client_Internal extends Request_Client {
/**
* #var array
*/
protected $_previous_environment;
/**
* Processes the request, executing the controller action that handles this
* request, determined by the [Route].
*
* 1. Before the controller action is called, the [Controller::before] method
* will be called.
* 2. Next the controller action will be called.
* 3. After the controller action is called, the [Controller::after] method
* will be called.
*
* By default, the output from the controller is captured and returned, and
* no headers are sent.
*
* $request->execute();
*
* #param Request $request
* #return Response
* #throws Kohana_Exception
* #uses [Kohana::$profiling]
* #uses [Profiler]
* #deprecated passing $params to controller methods deprecated since version 3.1
* will be removed in 3.2
*/
public function execute_request(Request $request)
{
// Create the class prefix
$prefix = 'controller_';
// Directory
$directory = $request->directory();
// Controller
$controller = $request->controller();
if ($directory)
{
// Add the directory name to the class prefix
$prefix .= str_replace(array('\\', '/'), '_', trim($directory, '/')).'_';
}
if (Kohana::$profiling)
{
// Set the benchmark name
$benchmark = '"'.$request->uri().'"';
if ($request !== Request::$initial AND Request::$current)
{
// Add the parent request uri
$benchmark .= ' « "'.Request::$current->uri().'"';
}
// Start benchmarking
$benchmark = Profiler::start('Requests', $benchmark);
}
// Store the currently active request
$previous = Request::$current;
// Change the current request to this request
Request::$current = $request;
// Is this the initial request
$initial_request = ($request === Request::$initial);
try
{
if ( ! class_exists($prefix.$controller))
{
throw new HTTP_Exception_404('The requested URL :uri was not found on this server.',
array(':uri' => $request->uri()));
}
// Load the controller using reflection
$class = new ReflectionClass($prefix.$controller);
if ($class->isAbstract())
{
throw new Kohana_Exception('Cannot create instances of abstract :controller',
array(':controller' => $prefix.$controller));
}
// Create a new instance of the controller
$controller = $class->newInstance($request, $request->response() ? $request->response() : $request->create_response());
$class->getMethod('before')->invoke($controller);
// Determine the action to use
/* ADDED */ if (strpos($request->action(), '_') !== false) throw new HTTP_Exception_404('The requested URL :uri was not found on this server.', array(':uri' => $request->uri()));
/* MODIFIED */ $action = str_replace('-', '_', $request->action()); /* ORIGINAL: $action = $request->action(); */
$params = $request->param();
// If the action doesn't exist, it's a 404
if ( ! $class->hasMethod('action_'.$action))
{
throw new HTTP_Exception_404('The requested URL :uri was not found on this server.',
array(':uri' => $request->uri()));
}
$method = $class->getMethod('action_'.$action);
$method->invoke($controller);
// Execute the "after action" method
$class->getMethod('after')->invoke($controller);
}
catch (Exception $e)
{
// Restore the previous request
if ($previous instanceof Request)
{
Request::$current = $previous;
}
if (isset($benchmark))
{
// Delete the benchmark, it is invalid
Profiler::delete($benchmark);
}
// Re-throw the exception
throw $e;
}
// Restore the previous request
Request::$current = $previous;
if (isset($benchmark))
{
// Stop the benchmark
Profiler::stop($benchmark);
}
// Return the response
return $request->response();
}
} // End Kohana_Request_Client_Internal
Then to add an action with hyphens, for example, "controller/my-action", create an action called "my_action()".
This method will also throw an error if the user tries to access "controller/my_action" (to avoid duplicate content).
I know some developers don't like this method but the advantage of it is that it doesn't rename the action, so if you check the current action it will be consistently called "my-action" everywhere. With the Route or lambda function method, the action will sometime be called "my_action", sometime "my-action" (since both methods rename the action).

Advice for implementing simple regex (for bbcode/geshi parsing)

I had made a personal note software in PHP so I can store and organize my notes and wished for a nice simple format to write them in.
I had done it in Markdown but found it was a little confusing and there was no simple syntax highlighting, so I did bbcode before and wished to implement that.
Now for GeSHi which I really wish to implement (the syntax highlighter), it requires the most simple code like this:
$geshi = new GeSHi($sourcecode, $language);
$geshi->parse_code();
Now this is the easy part , but what I wish to do is allow my bbcode to call it.
My current regular expression to match a made up [syntax=cpp][/syntax] bbcode is the following:
preg_replace('#\[syntax=(.*?)\](.*?)\[/syntax\]#si' , 'geshi(\\2,\\1)????', text);
You will notice I capture the language and the content, how on earth would I connect it to the GeSHi code?
preg_replace seems to just be able to replace it with a string not an 'expression', I am not sure how to use those two lines of code for GeSHi up there with the captured data..
I really am excited about this project and wish to overcome this.
I wrote this class a while back, the reason for the class was to allow easy customization / parsing. Maybe a little overkill, but works well and I needed it overkill for my application. The usage is pretty simple:
$geshiH = new Geshi_Helper();
$text = $geshiH->geshi($text); // this assumes that the text should be parsed (ie inline syntaxes)
---- OR ----
$geshiH = new Geshi_Helper();
$text = $geshiH->geshi($text, $lang); // assumes that you have the language, good for a snippets deal
I had to do some chopping from other custom items I had, but pending no syntax errors from the chopping it should work. Feel free to use it.
<?php
require_once 'Geshi/geshi.php';
class Geshi_Helper
{
/**
* #var array Array of matches from the code block.
*/
private $_codeMatches = array();
private $_token = "";
private $_count = 1;
public function __construct()
{
/* Generate a unique hash token for replacement) */
$this->_token = md5(time() . rand(9999,9999999));
}
/**
* Performs syntax highlights using geshi library to the content.
*
* #param string $content - The context to parse
* #return string Syntax Highlighted content
*/
public function geshi($content, $lang=null)
{
if (!is_null($lang)) {
/* Given the returned results 0 is not set, adding the "" should make this compatible */
$content = $this->_highlightSyntax(array("", strtolower($lang), $content));
}else {
/* Need to replace this prior to the code replace for nobbc */
$content = preg_replace('~\[nobbc\](.+?)\[/nobbc\]~ie', '\'[nobbc]\' . strtr(\'$1\', array(\'[\' => \'[\', \']\' => \']\', \':\' => \':\', \'#\' => \'#\')) . \'[/nobbc]\'', $content);
/* For multiple content we have to handle the br's, hence the replacement filters */
$content = $this->_preFilter($content);
/* Reverse the nobbc markup */
$content = preg_replace('~\[nobbc\](.+?)\[/nobbc\]~ie', 'strtr(\'$1\', array(\'&#91;\' => \'[\', \'&#93;\' => \']\', \'&#58;\' => \':\', \'&#64;\' => \'#\'))', $content);
$content = $this->_postFilter($content);
}
return $content;
}
/**
* Performs syntax highlights using geshi library to the content.
* If it is unknown the number of blocks, use highlightContent
* instead.
*
* #param string $content - The code block to parse
* #param string $language - The language to highlight with
* #return string Syntax Highlighted content
* #todo Add any extra / customization styling here.
*/
private function _highlightSyntax($contentArray)
{
$codeCount = $contentArray[1];
/* If the count is 2 we are working with the filter */
if (count($contentArray) == 2) {
$contentArray = $this->_codeMatches[$contentArray[1]];
}
/* for default [syntax] */
if ($contentArray[1] == "")
$contentArray[1] = "php";
/* Grab the language */
$language = (isset($contentArray[1]))?$contentArray[1]:'text';
/* Remove leading spaces to avoid problems */
$content = ltrim($contentArray[2]);
/* Parse the code to be highlighted */
$geshi = new GeSHi($content, strtolower($language));
return $geshi->parse_code();
}
/**
* Substitute the code blocks for formatting to be done without
* messing up the code.
*
* #param array $match - Referenced array of items to substitute
* #return string Substituted content
*/
private function _substitute(&$match)
{
$index = sprintf("%02d", $this->_count++);
$this->_codeMatches[$index] = $match;
return "----" . $this->_token . $index . "----";
}
/**
* Removes the code from the rest of the content to apply other filters.
*
* #param string $content - The content to filter out the code lines
* #return string Content with code removed.
*/
private function _preFilter($content)
{
return preg_replace_callback("#\s*\[syntax=(.*?)\](.*?)\[/syntax\]\s*#siU", array($this, "_substitute"), $content);
}
/**
* Replaces the code after the filters have been ran.
*
* #param string $content - The content to replace the code lines
* #return string Content with code re-applied.
*/
private function _postFilter($content)
{
/* using dashes to prevent the old filtered tag being escaped */
return preg_replace_callback("/----\s*" . $this->_token . "(\d{2})\s*----/si", array($this, "_highlightSyntax"), $content);
}
}
?>
It looks to me like you already got the regex right. Your problem lies in the invocation, so I suggest making a wrapper function:
function geshi($src, $l) {
$geshi = new GeSHi($sourcecode, $language);
$geshi->parse_code();
return $geshi->how_do_I_get_the_results();
}
Now this would normally suffice, but the source code is likely to contain single or dobule quotes itself. Therefore you cannot write preg_replace(".../e", "geshi('$2','$1')", ...) as you would need. (Note that '$1' and '$2' need quotes because preg_replace just substitutes the $1,$2 placeholders, but this needs to be valid php inline code).
That's why you need to use preg_replace_callback to avoid escaping issues in the /e exec replacement code.
So for example:
preg_replace_callback('#\[syntax=(.*?)\](.*?)\[/syntax\]#si' , 'geshi_replace', $text);
And I'd make a second wrapper, but you can combine it with the original code:
function geshi_replace($uu) {
return geshi($uu[2], $uu[1]);
}
Use preg_match:
$match = preg_match('#\[syntax=(.*?)\](.*?)\[/syntax\]#si', $text);
$geshi = new GeSHi($match[2], $match[1]);

Categories