For example, I have this kind of code:
<?php
/**
* Order
*
* The WooCommerce order class handles order data.
*
* #class WC_Order
* #version 1.6.4
* #package WooCommerce/Classes
* #category Class
* #author WooThemes
*/
class WC_Order {
/** #public int Order (post) ID */
public $id;
/** #public string Order status. */
public $status;
/** #public string Order date (placed). */
public $order_date;
/** #public string Order date (paid). */
public $modified_date;
/** #public string Note added by the customer. */
public $customer_note;
/** #public array Order (post) meta/custom fields. */
public $order_custom_fields;
global $wpdb, $woocommerce;
if ( empty( $type ) )
$type = array( 'line_item' );
if ( ! is_array( $type ) )
$type = array( $type );
$items = $this->get_items( $type );
$count = 0;
foreach ( $items as $item ) {
if ( ! empty( $item['qty'] ) )
$count += $item['qty'];
else
$count ++;
}
return apply_filters( 'woocommerce_get_item_count', $count, $type, $this );
}
/**
* Return an array of fees within this order.
*
* #access public
* #return array
*/
public function get_fees() {
return $this->get_items( 'fee' );
}
/**
* Return an array of taxes within this order.
*
* #access public
* #return void
*/
public function get_taxes() {
return $this->get_items( 'tax' );
}
/**
* Get taxes, merged by code, formatted ready for output.
*
* #access public
* #return void
*/
public function get_tax_totals() {
$taxes = $this->get_items( 'tax' );
$tax_totals = array();
foreach ( $taxes as $key => $tax ) {
$code = $tax[ 'name' ];
if ( ! isset( $tax_totals[ $code ] ) ) {
$tax_totals[ $code ] = new stdClass();
$tax_totals[ $code ]->amount = 0;
}
$tax_totals[ $code ]->is_compound = $tax[ 'compound' ];
$tax_totals[ $code ]->label = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
$tax_totals[ $code ]->amount += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
$tax_totals[ $code ]->formatted_amount = woocommerce_price( $tax_totals[ $code ]->amount );
}
return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
}
/**
* has_meta function for order items.
*
* #access public
* #return array of meta data
*/
public function has_meta( $order_item_id ) {
global $wpdb;
return $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value, meta_id, order_item_id
FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
ORDER BY meta_key,meta_id", absint( $order_item_id ) ), ARRAY_A );
}
/**
* Get order item meta.
*
* #access public
* #param mixed $item_id
* #param string $key (default: '')
* #param bool $single (default: false)
* #return void
*/
public function get_item_meta( $order_item_id, $key = '', $single = false ) {
return get_metadata( 'order_item', $order_item_id, $key, $single );
}
I want to match all Wordpress hooks: "do_action" and "apply_filters" with three options:
apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this ), file, line number
An example of what i'm trying to do can be seen here:
http://etivite.com/api-hooks/buddypress/trigger/apply_filters/bp_get_total_mention_count_for_user/
http://adambrown.info/p/wp_hooks/hook/activated_plugin?version=3.6&file=wp-admin/includes/plugin.php
I did try to pull something out but with no success:
<?php
$path = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/iphorm-form-builder';
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename) {
if (substr($filename, -3) == 'php') {
$file = file($filename);
if ($file !== false) {
$matches1 = preg_grep( '/do_action\((.+)\);/', $file);
$matches2 = preg_grep( '/apply_filters\((.+)\);/', $file );
$arr = array_filter(array_merge($matches1, $matches2));
$out = '';
echo "found in $filename:";
echo "<pre>";
foreach ($arr as $key => $value) {
$out .= $file[$key-2];
$out .= $file[$key-1];
$out .= $file[$key];
$out .= $file[$key+1];
$out .= $file[$key+2];
}
echo htmlentities($out);
echo "</pre>";
} else {
echo "failed reading to array";
}
}
}
This can be done very simply by taking advantage of built-in shell commands.
<?php
$path = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/iphorm-form-builder';
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename) {
if (substr($filename, -3) == 'php') {
$context = shell_exec('grep -H -n -C4 do_action ' . escapeshellarg($filename));
if (!empty($context)) {
echo "found in $filename:";
echo "<pre>";
echo htmlentities($context);
echo "</pre>";
} else {
echo "failed reading to array";
}
}
}
Relevant documentation:
php shell_exec
Execute command via shell and return the complete output as a string
php escapeshellarg
Escape a string to be used as a shell argument
bash grep
Grep searches the named input FILEs (or standard input if no files are
named, or the file name - is given) for lines containing a match to the
given PATTERN. By default, grep prints the matching lines.
-C NUM, --context=NUM
Print NUM lines of output context. Places a line containing --
between contiguous groups of matches.
-H, --with-filename
Print the filename for each match.
-n, --line-number
Prefix each line of output with the line number within its input
file.
Edit
Depending on how many directories and files you have in your project, it may not be performant. You're basically creating a new process in a new shell for each file. That's not great. If you'd rather get a big dump of data and parse it out later, do this instead:
<?php
$path = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/iphorm-form-builder';
$grep = shell_exec('grep --include=*.php -RHn -C4 do_action ' . escapeshellarg($path));
$matches = explode("\n--\n", $grep);
if (empty($matches)) {
echo "failed reading to array";
}
else {
foreach ($matches as $match) {
echo "<pre>";
echo htmlentities($match);
echo "</pre>";
}
}
You don't need to loop through file data line by line. Just read the data in a variable and apply regex:
$data = file_get_contents( $filename );
if (preg_match_all(
'~((?:[^\n]*\n){0,4}) *do_action\s*\(\s*([^)]+)\s*\)\s*;[^\n]*\n((?:[^\n]*\n){0,4})~',
$data, $arr)) {
// match found, now dump the results
// $arr[1] will print 4 lines before the match
// $arr[2] will print function arguments for do_action
// $arr[3] will print 4 lines after the match
print_r($arr);
}
This can not be done by regular expressions alone given the limitations of PHP's regular expression engine. Specifically, and it came as a surprise to me, you can not have variable length look behinds.
The reason you need look behinds is if you had the occurrence of do_action or apply_filters on consecutive lines, then the first match will prevent the second match if you had no look aheads, and even if you use look aheads, there is no way to get the previous two rows of the second match without the look behinds. Not to mention I don't even know if you can even include the contents of a look around assertion in the result.
This solution creates a regular expression to find the lines in which the function occurs, and then uses two more regexes to find the lines before and after. I took care to watch out for the start and end of file when designing the regexes.
$offset = 0;
while (preg_match('/^.*?(?:apply_filters|do_action)\s*\(.+?\)\s*;.*(?:\n\r|\n|\r|$)/m', $file, $match, PREG_OFFSET_CAPTURE, $offset))
{
$index = $match[0][1];
$offset = $index + strlen($match[0][0]);
$hasBefore = preg_match('/(?:.*(?:\n\r|\n|\r).*$)|[^\n\r].*$/',
substr($file, 0, $index), $beforeMatch);
$hasAfter = preg_match('/^(?:.*(?:\n\r|\n|\r|$)){0,2}/',
substr($file, $offset), $afterMatch);
if ($hasBefore) print_r($beforeMatch);
print_r($match[0][0]);
if ($hasAfter) print_r($afterMatch);
}
phpFiddle
This only displays two lines before and two lines after. You can use repetition if you want more, but it appears to me from the attempted solution that this was what the o.p. really wanted.
Related
I have to change filenames to images/filename and files/filename in Wordpress and I ended up using WP-CLI's search-replace function.
Here is the shell command I execute:
wp search-replace '(")(5\.jpg)' 'images/${2}' wp_postmeta --regex --regex-delimiter='/' --dry-run --skip-columns=meta_key --debug=true
This gives me back message: 0 replacements to be made.
Here is one entry from the database:
a:5:{s:5:"width";i:2048;s:6:"height";i:1536;s:4:"file";s:5:"5.jpg";s:5:"sizes";a:4:{s:9:"thumbnail";a:4:{s:4:"file";s:13:"5-150x150.jpg";s:5:"width";i:150;s:6:"height";i:150;s:9:"mime-type";s:10:"image/jpeg";}s:6:"medium";a:4:{s:4:"file";s:13:"5-300x225.jpg";s:5:"width";i:300;s:6:"height";i:225;s:9:"mime-type";s:10:"image/jpeg";}s:12:"medium_large";a:4:{s:4:"file";s:13:"5-768x576.jpg";s:5:"width";i:768;s:6:"height";i:576;s:9:"mime-type";s:10:"image/jpeg";}s:5:"large";a:4:{s:4:"file";s:14:"5-1024x768.jpg";s:5:"width";i:1024;s:6:"height";i:768;s:9:"mime-type";s:10:"image/jpeg";}}s:10:"image_meta";a:12:{s:8:"aperture";s:1:"0";s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:1:"0";s:3:"iso";s:1:"0";s:13:"shutter_speed";s:1:"0";s:5:"title";s:0:"";s:11:"orientation";s:1:"0";s:8:"keywords";a:0:{}}}
I created a custom PHP script as the following:
$data = 'a:5:{s:5:"width";i:2048;s:6:"height";i:1536;s:4:"file";s:5:"5.jpg";s:5:"sizes";a:4:{s:9:"thumbnail";a:4:{s:4:"file";s:13:"5-150x150.jpg";s:5:"width";i:150;s:6:"height";i:150;s:9:"mime-type";s:10:"image/jpeg";}s:6:"medium";a:4:{s:4:"file";s:13:"5-300x225.jpg";s:5:"width";i:300;s:6:"height";i:225;s:9:"mime-type";s:10:"image/jpeg";}s:12:"medium_large";a:4:{s:4:"file";s:13:"5-768x576.jpg";s:5:"width";i:768;s:6:"height";i:576;s:9:"mime-type";s:10:"image/jpeg";}s:5:"large";a:4:{s:4:"file";s:14:"5-1024x768.jpg";s:5:"width";i:1024;s:6:"height";i:768;s:9:"mime-type";s:10:"image/jpeg";}}s:10:"image_meta";a:12:{s:8:"aperture";s:1:"0";s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:1:"0";s:3:"iso";s:1:"0";s:13:"shutter_speed";s:1:"0";s:5:"title";s:0:"";s:11:"orientation";s:1:"0";s:8:"keywords";a:0:{}}}';
if (preg_match('/(")(5\.jpg)/', $data)) {
echo "A match was found.";
} else {
echo "A match was not found.";
}
echo '<br>';
$data = preg_replace( '/(")(5\.jpg)/', 'images/${2}', $data );
var_dump($data);
This replaces the data fine, I get result as
a:5:{s:5:"width";i:2048;s:6:"height";i:1536;s:4:"file";s:5:"images/5.jpg" ...
This is the source code of WP-CLI search-replace:
<?php
namespace WP_CLI;
class SearchReplacer {
private $from, $to;
private $recurse_objects;
private $regex;
private $regex_flags;
private $regex_delimiter;
private $logging;
private $log_data;
private $max_recursion;
/**
* #param string $from String we're looking to replace.
* #param string $to What we want it to be replaced with.
* #param bool $recurse_objects Should objects be recursively replaced?
* #param bool $regex Whether `$from` is a regular expression.
* #param string $regex_flags Flags for regular expression.
* #param string $regex_delimiter Delimiter for regular expression.
* #param bool $logging Whether logging.
*/
function __construct( $from, $to, $recurse_objects = false, $regex = false, $regex_flags = '', $regex_delimiter = '/', $logging = false ) {
$this->from = $from;
$this->to = $to;
$this->recurse_objects = $recurse_objects;
$this->regex = $regex;
$this->regex_flags = $regex_flags;
$this->regex_delimiter = $regex_delimiter;
$this->logging = $logging;
$this->clear_log_data();
// Get the XDebug nesting level. Will be zero (no limit) if no value is set
$this->max_recursion = intval( ini_get( 'xdebug.max_nesting_level' ) );
}
/**
* Take a serialised array and unserialise it replacing elements as needed and
* unserialising any subordinate arrays and performing the replace on those too.
* Ignores any serialized objects unless $recurse_objects is set to true.
*
* #param array|string $data The data to operate on.
* #param bool $serialised Does the value of $data need to be unserialized?
*
* #return array The original array with all elements replaced as needed.
*/
function run( $data, $serialised = false ) {
return $this->_run( $data, $serialised );
}
/**
* #param int $recursion_level Current recursion depth within the original data.
* #param array $visited_data Data that has been seen in previous recursion iterations.
*/
private function _run( $data, $serialised, $recursion_level = 0, $visited_data = array() ) {
// some unseriliased data cannot be re-serialised eg. SimpleXMLElements
try {
if ( $this->recurse_objects ) {
// If we've reached the maximum recursion level, short circuit
if ( $this->max_recursion != 0 && $recursion_level >= $this->max_recursion ) {
return $data;
}
if ( is_array( $data ) || is_object( $data ) ) {
// If we've seen this exact object or array before, short circuit
if ( in_array( $data, $visited_data, true ) ) {
return $data; // Avoid infinite loops when there's a cycle
}
// Add this data to the list of
$visited_data[] = $data;
}
}
if ( is_string( $data ) && ( $unserialized = #unserialize( $data ) ) !== false ) {
$data = $this->_run( $unserialized, true, $recursion_level + 1 );
}
elseif ( is_array( $data ) ) {
$keys = array_keys( $data );
foreach ( $keys as $key ) {
$data[ $key ]= $this->_run( $data[$key], false, $recursion_level + 1, $visited_data );
}
}
elseif ( $this->recurse_objects && is_object( $data ) ) {
foreach ( $data as $key => $value ) {
$data->$key = $this->_run( $value, false, $recursion_level + 1, $visited_data );
}
}
else if ( is_string( $data ) ) {
if ( $this->logging ) {
$old_data = $data;
}
if ( $this->regex ) {
$search_regex = $this->regex_delimiter;
$search_regex .= $this->from;
$search_regex .= $this->regex_delimiter;
$search_regex .= $this->regex_flags;
$data = preg_replace( $search_regex, $this->to, $data );
} else {
$data = str_replace( $this->from, $this->to, $data );
}
if ( $this->logging && $old_data !== $data ) {
$this->log_data[] = $old_data;
}
}
if ( $serialised )
return serialize( $data );
} catch( Exception $error ) {
}
return $data;
}
/**
* Gets existing data saved for this run when logging.
* #return array Array of data strings, prior to replacements.
*/
public function get_log_data() {
return $this->log_data;
}
/**
* Clears data stored for logging.
*/
public function clear_log_data() {
$this->log_data = array();
}
}
I use the exact same preg_replace as used here,
$data = preg_replace( $search_regex, $this->to, $data );
Why this is not working in WP-CLI?
I'm receiving this waring on every page of my website, the error message is as below:
the code that the waring is refering to is like below:
<?php
/**
* Order the array by a key
*
* #param array $a The array to sort
* #param string $subkey The key used for the sorting
* #return array Array sorted
*
* #since 1.0
*/
function yiw_subval_sort( $a, $subkey ) {
if( is_array( $a ) AND ! empty( $a ) ) {
foreach( $a as $k => $v ) {
$b[$k] = strtolower( $v[$subkey] );
}
asort( $b );
foreach( $b as $key => $val ) {
$c[] = $a[$key];
}
return $c;
}
return $a;
}
?>
line 19 is this line:
$b[$k] = strtolower( $v[$subkey] );
I do see there's couple of questions on stack overflow point to such problem but I've tried everything, nothing works, please help! Thanks in advance!
Check to see if the $subkey element exists, like so:
if (isset($v[$subkey])) {
$b[$k] = strtolower( $v[$subkey] );
} else {
$b[$k] = '';
}
This should also resolve the header warning by preventing the error text.
I have index ponter, for example 5-1-key2-3.
And my array has its address:
array(
'5'=>array(
'1'=>array(
'key2'=>array(
'3'=>'here it is - 5-1-key2-3 key address'
)
)
)
)
which equals to
$arr[5][1][key2][3]='here it is - 5-1-key2-3 key address';
I know I can build the recursive function to access this value.
But I'm curious is it possible to achieve this without recursion and building user's functions and/or loops.
Probably it can be done with variable variables feature in php.
You can use this code
$keys = explode('-', '5-1-key2-3');
// start from the root of the array and progress through the elements
$temp = $arr;
foreach ($keys as $key_value)
{
$temp = $temp[$key_value];
}
// this will give you $arr["5"]["1"]["key2"]["3"] element value
echo $temp;
modifications after I got better understanding of the question I think you can do it with eval:
<?php
function getArrValuesFromString($string, $arr) {
$stringArr = '$arr[\'' . str_replace('-', "']['", $string) . '\']';
eval("\$t = " . $stringArr . ";");
return $t;
}
$arr[5][1]['key2'][3] = '1here it is - 5-1-key2-3 key address';
$string = '5-1-key2-3';
echo getArrValuesFromString($string, $arr); //1here it is - 5-1-key2-3 key address
EDIT :
Here is a way I deprecate so much, because of security, but if you are sure of what you are doing :
$key = 'a-b-c-d';
$array = <<your array>>;
$keys = explode('-', $key);
// we can surely do something better like checking for each one if its a string or int then adding or not the `'`
$final_key = "['".implode("']['", $keys)."']";
$result = eval("return \$array{$final_key};");
There is a class I wrote inspired from something I read on the web don't really remember where but anyway, this can helps you :
/**
* Class MultidimensionalHelper
*
* Some help about multidimensional arrays
* like dynamic array_key_exists, set, and get functions
*
* #package Core\Utils\Arrays
*/
class MultidimensionalHelper
{
protected $keySeparator = '.';
/**
* #return string
*/
public function keySeparator()
{
return $this->keySeparator;
}
/**
* #param string $keySeparator
*/
public function setKeySeparator($keySeparator)
{
$this->keySeparator = $keySeparator;
}
/**
* Multidimensional array dynamic array_key_exists
*
* #param $key String Needle
* #param $array Array Haystack
* #return bool True if found, false either
*/
public function exists($key, $array)
{
$keys = explode($this->keySeparator(), $key);
$tmp = $array;
foreach($keys as $k)
{
if(!array_key_exists($k, $tmp))
{
return false;
}
$tmp = $tmp[$k];
}
return true;
}
/**
* Multidimensional array dynamic getter
*
*
* #param $key String Needle
* #param $array Array Haystack
* #return mixed Null if key not exists or the content of the key
*/
public function get($key, $array)
{
$keys = explode($this->keySeparator(), $key);
$lkey = array_pop($keys);
$tmp = $array;
foreach($keys as $k)
{
if(!isset($tmp[$k]))
{
return null;
}
$tmp = $tmp[$k];
}
return $tmp[$lkey];
}
/**
* Multidimensional array dynamic setter
*
* #param String $key
* #param Mixed $value
* #param Array $array Array to modify
* #param Bool $return
* #return Array If $return is set to TRUE (bool), this function
* returns the modified array instead of directly modifying it.
*/
public function set($key, $value, &$array)
{
$keys = explode($this->keySeparator(), $key);
$lkey = array_pop($keys);
$tmp = &$array;
foreach($keys as $k)
{
if(!isset($tmp[$k]) || !is_array($tmp[$k]))
{
$tmp[$k] = array();
}
$tmp = &$tmp[$k];
}
$tmp[$lkey] = $value;
unset($tmp);
}
}
Then use :
$MDH = new MultidimensionalHelper();
$MDH->setKeySeparator('-');
$arr = [
'a' => [
'b' => [
'c' => 'good value',
],
'c' => 'wrong value',
],
'b' => [
'c' => 'wrong value',
]
];
$key = 'a-b-c';
$val = $MDH->get($key, $arr);
var_dump($val);
Here is the content of the get function if you don't find it in Class code :
public function get($key, $array)
{
$keys = explode($this->keySeparator(), $key);
$lkey = array_pop($keys);
$tmp = $array;
foreach($keys as $k)
{
if(!isset($tmp[$k]))
{
return null;
}
$tmp = $tmp[$k];
}
return $tmp[$lkey];
}
I have a problem with my code that i dont understand.
can anyone help please ?
$query="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
$seq=chunk_split($query,50,"<br />");
$truecol = "<span style=\"color: hsl(0,100%,25%);\">";
function colorSequence ($seq,$position,$truecol,$TFBSlength){
$nucleotides = str_split($seq);
foreach ($nucleotides as $index => $nucl){
if ($index == $position){
echo $truecol;
}
if ($index == $position + $TFBSlength){
echo "</span>";
}
echo $nucl;
}
echo "\n";
}
colorSequence($seq,49,$truecol,1);
?>
This is my code. Basically I wan the output code to look like this:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA(red)A(/red)
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
and it does when i run the function colorSequence($seq,49,$truecol,1);
however if i run colorSequence($seq,49,$truecol,3); i get this:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA< span="">r />AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC<>(all letters from letter 49 are red, even tho i only want 3 letters form 49 to 51 to be red).
can anyone fix this please ?
$query="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
$seq=chunk_split($query,50,'');
$truecol = "<span style=\"color: hsl(0,100%,25%);\">";
function colorSequence ($seq,$position,$truecol,$TFBSlength){
$nucleotides = str_split($seq);
foreach ($nucleotides as $index => $nucl){
if ($index == $position){
echo $truecol;
}
if ($index == $position + $TFBSlength){
echo "</span>";
}
if(($index%50)==0){
echo '<br>';
}
echo $nucl;
}
echo "\n";
}
colorSequence($seq,49,$truecol,1);
echo '<hr>';
colorSequence($seq,49,$truecol,3)
if you print_r($nucleotides), you will see that
Array
(
......
[49] => A
[50] => <
[51] => b
[52] => r
[53] =>
......
)
so you are inserting the <span> into <br /> which destroys the following html...
The span tag added by your function breaks the HTML created by chunk_split.
When you modify the DNS string $query you need to differ between the position of the nucleotides and general string offsets.
While at first both are identical, the more you add to the string, the more this differs.
If you encapsulate the string into an object of it's own that take care of the HTML tags and don't count them (including surrounding whitespace around the tags), things become easy again:
$query = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
// wrap line: 11 lines à 50 nucleotids
$seq = chunk_split($query, 50, "<br />\n");
// get help from sequence object
$sequence = new AmendSequence($seq);
// some HTML comments for demonstration purposes
$sequence->insertAt(0, "<!-- first line -->\n");
$sequence->insertAt(50, "<!-- second line -->\n", TRUE); # TRUE: Place after <br />
$sequence->insertAt(75, "<!-- inside second line -->");
$sequence->insertAt(550, "<!-- at end -->", TRUE); # TRUE: Place after <br />
// colorize
$color = '<span style="color: hsl(0,100%,25%);">';
$sequence->insertAt(49, $color);
$sequence->insertAt(50, '</span>');
printf("Sequence with %d nucleotids:\n", count($sequence)); # count gives length w/o amends
echo $sequence, "\n"; # that prints your sequence with all amends
Which creates the following output:
Sequence with 550 nucleotids:
<!-- first line -->
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<span style="color: hsl(0,100%,25%);">A</span><br />
<!-- second line -->
AAAAAAAAAAAAAAAAAAAAAAAAA<!-- inside second line -->AAAAAAAAAAAAAAAAAAAAAAAAA<br />
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<br />
AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB<br />
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB<br />
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB<br />
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCC<br />
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC<br />
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC<br />
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC<br />
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC<br />
<!-- at end -->
So the actual magic here is the insertAt method which accepts the offset position of the nucledotide and calculates the string offset for it. It's all encapsulated into a class of it's own which probably is a bit too much for the beginning, however the real action is dividing the string that contains the DNA and the HTML into segements of DNS only and getting their actual string offset. The full source code:
<?php
/**
* #link http://stackoverflow.com/questions/10446162/how-to-wordwrap-with-different-length-string-modification
*/
/**
* Treat text with "tags" as text without tags when insertAt() is called.
*/
class AmendSequence implements IteratorAggregate, Countable
{
/**
* regex pattern for a tag
*/
const TAG = '\s*<[^>]*>\s*';
/**
* #var string
*/
private $query;
/**
* #param string $query
*/
public function __construct($query = '')
{
$this->setQuery($query);
}
/**
* #param int $offset
* #param string $string
* #param bool $after (optional) prefer after next tag instead before
*/
public function insertAt($offset, $string, $after = FALSE)
{
$offset = $this->translate($offset, $after);
$this->query = substr_replace($this->query, $string, $offset, 0);
}
/**
* Translate virtual offset to string offset
* #param int $virtualOffset
* #return int
* #throws InvalidArgumentException
*/
public function translate($virtualOffset, $after)
{
if ($virtualOffset < 0) throw new InvalidArgumentException(sprintf('Offset can not be lower than 0, is %d.', $virtualOffset));
$virtualCurrent = 0;
foreach ($this as $segment) {
list(, $current, $length) = $segment;
$delta = ($virtualOffset - $virtualCurrent) - $length;
if ($delta < 0 || ($delta === 0 && !$after)) {
return $current + $length + $delta;
}
$virtualCurrent += $length;
}
if ($virtualCurrent === $virtualOffset && $after) {
return strlen($this->query);
}
throw new InvalidArgumentException(sprintf('Offset can not be larger than %d, is %d.', $virtualCurrent, $virtualOffset));
}
/**
* #return array
*/
public function getSegments()
{
$segments = preg_split('/' . self::TAG . '/', $this->query, 0, PREG_SPLIT_OFFSET_CAPTURE | PREG_SPLIT_NO_EMPTY);
foreach ($segments as &$segment) {
$segment[2] = strlen($segment[0]);
}
return $segments;
}
public function getSequence()
{
$buffer = '';
foreach ($this as $segment) {
$buffer .= $segment[0];
}
return $buffer;
}
/**
* #return string
*/
public function getQuery()
{
return $this->query;
}
/**
* #param string $query
*/
public function setQuery($query)
{
$this->query = (string)$query;
}
/**
* Retrieve an external iterator
* #link http://php.net/manual/en/iteratoraggregate.getiterator.php
* #return Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b>
*/
public function getIterator()
{
return new ArrayIterator($this->getSegments());
}
/**
* #link http://php.net/manual/en/countable.count.php
* #return int The custom count as an integer.
*/
public function count()
{
$length = 0;
foreach ($this as $segment) {
$length += $segment[2];
}
return $length;
}
/**
* #return string
*/
public function __toString()
{
return $this->query;
}
}
$query = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
// wrap line: 11 lines à 50 nucleotids
$seq = chunk_split($query, 50, "<br />\n");
// get help from sequence object
$sequence = new AmendSequence($seq);
// some HTML comments for demonstration purposes
$sequence->insertAt(0, "<!-- first line -->\n");
$sequence->insertAt(50, "<!-- second line -->\n", TRUE); # TRUE: Place after <br />
$sequence->insertAt(75, "<!-- inside second line -->");
$sequence->insertAt(550, "<!-- at end -->", TRUE); # TRUE: Place after <br />
// colorize
$color = '<span style="color: hsl(0,100%,25%);">';
$sequence->insertAt(49, $color);
$sequence->insertAt(50, '</span>');
printf("Sequence with %d nucleotids:\n", count($sequence)); # count gives length w/o amends
echo $sequence, "\n"; # that prints your sequence with all amends
echo $sequence->getSequence(); # without the amends
Feel free now to colorize as many parts as you like - with or without other HTML already inside the sequence.
Given a url, and a query string, how can I get the url resulting from the combination of the query string with the url?
I'm looking for functionality similar to .htaccess's qsa. I realize this would be fairly trivial to implement completely by hand, however are there built-in functions that deal with query strings which could either simplify or completely solve this?
Example input/result sets:
Url="http://www.example.com/index.php/page?a=1"
QS ="?b=2"
Result="http://www.example.com/index.php/page?a=1&b=2"
-
Url="page.php"
QS ="?b=2"
Result="page.php?b=2"
How about something that uses no PECL extensions and isn't a huge set of copied-and-pasted functions? It's still a tad complex because you're splicing together two query strings and want to do it in a way that isn't just $old .= $new;
We'll use parse_url to extract the query string from the desired url, parse_str to parse the query strings you wish to join, array_merge to join them together, and http_build_query to create the new, combined string for us.
// Parse the URL into components
$url = 'http://...';
$url_parsed = parse_url($url);
$new_qs_parsed = array();
// Grab our first query string
parse_str($url_parsed['query'], $new_qs_parsed);
// Here's the other query string
$other_query_string = 'that=this&those=these';
$other_qs_parsed = array();
parse_str($other_query_string, $other_qs_parsed);
// Stitch the two query strings together
$final_query_string_array = array_merge($new_qs_parsed, $other_qs_parsed);
$final_query_string = http_build_query($final_query_string_array);
// Now, our final URL:
$new_url = $url_parsed['scheme']
. '://'
. $url_parsed['host']
. $url_parsed['path']
. '?'
. $final_query_string;
You can get the query string part from url using:
$_SERVER['QUERY_STRING']
and then append it to url normally.
If you want to specify your own custom variables in query string, have a look at:
http_build_query
This is a series of functions taken from the WordPress "framework" that will do it, but this could quite well be too much:
add_query_arg()
/**
* Retrieve a modified URL query string.
*
* You can rebuild the URL and append a new query variable to the URL query by
* using this function. You can also retrieve the full URL with query data.
*
* Adding a single key & value or an associative array. Setting a key value to
* emptystring removes the key. Omitting oldquery_or_uri uses the $_SERVER
* value.
*
* #since 1.0
*
* #param mixed $param1 Either newkey or an associative_array
* #param mixed $param2 Either newvalue or oldquery or uri
* #param mixed $param3 Optional. Old query or uri
* #return string New URL query string.
*/
public function add_query_arg() {
$ret = '';
if ( is_array( func_get_arg(0) ) ) {
$uri = ( #func_num_args() < 2 || false === #func_get_arg( 1 ) ) ? $_SERVER['REQUEST_URI'] : #func_get_arg( 1 );
} else {
$uri = ( #func_num_args() < 3 || false === #func_get_arg( 2 ) ) ? $_SERVER['REQUEST_URI'] : #func_get_arg( 2 );
}
if ( $frag = strstr( $uri, '#' ) ) {
$uri = substr( $uri, 0, -strlen( $frag ) );
} else {
$frag = '';
}
if ( preg_match( '|^https?://|i', $uri, $matches ) ) {
$protocol = $matches[0];
$uri = substr( $uri, strlen( $protocol ) );
} else {
$protocol = '';
}
if ( strpos( $uri, '?' ) !== false ) {
$parts = explode( '?', $uri, 2 );
if ( 1 == count( $parts ) ) {
$base = '?';
$query = $parts[0];
} else {
$base = $parts[0] . '?';
$query = $parts[1];
}
} elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {
$base = $uri . '?';
$query = '';
} else {
$base = '';
$query = $uri;
}
parse_str( $query, $qs );
if ( get_magic_quotes_gpc() )
$qs = format::stripslashes_deep( $qs );
$qs = format::urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
if ( is_array( func_get_arg( 0 ) ) ) {
$kayvees = func_get_arg( 0 );
$qs = array_merge( $qs, $kayvees );
} else {
$qs[func_get_arg( 0 )] = func_get_arg( 1 );
}
foreach ( ( array ) $qs as $k => $v ) {
if ( $v === false )
unset( $qs[$k] );
}
$ret = http_build_query( $qs, '', '&' );
$ret = trim( $ret, '?' );
$ret = preg_replace( '#=(&|$)#', '$1', $ret );
$ret = $protocol . $base . $ret . $frag;
$ret = rtrim( $ret, '?' );
return $ret;
}
stripslashes_deep()
/**
* Navigates through an array and removes slashes from the values.
*
* If an array is passed, the array_map() function causes a callback to pass the
* value back to the function. The slashes from this value will removed.
*
* #since 1.0
*
* #param array|string $value The array or string to be stripped
* #return array|string Stripped array (or string in the callback).
*/
function stripslashes_deep( $value ) {
return is_array( $value ) ? array_map( array('self', 'stripslashes_deep'), $value ) : stripslashes( $value );
}
urlencode_deep()
/**
* Navigates through an array and encodes the values to be used in a URL.
*
* Uses a callback to pass the value of the array back to the function as a
* string.
*
* #since 1.0
*
* #param array|string $value The array or string to be encoded.
* #return array|string $value The encoded array (or string from the callback).
*/
public function urlencode_deep( $value ) {
return is_array($value) ? array_map( array('self', 'urlencode_deep'), $value) : urlencode($value);
}
THere is no built-in function to do this. However, you can use this function from http PECL extension,
http://usphp.com/manual/en/function.http-build-url.php
For example,
$url = http_build_url("http://www.example.com/index.php/page?a=1",
array(
"b" => "2"
)
);
So what happens if the urls conflict? If both urls contain a b= component in the querystring? You'd need to decided which holds sway.
Here's a chunk of code that does what you want, parsing each string as a url, then extracting the query url part and implode() ing them back together.
$url="http://www.example.com/index.php/page?a=1";
$qs ="?b=2";
$url_parsed = parse_url($url);
$qs_parsed = parse_url($qs);
$args = array(
$url_parsed['query'],
$qs_parsed['query'],
);
$new_url = $url_parsed['scheme'];
$new_url .= '://';
$new_url .= $url_parsed['host'];
$new_url .= $url_parsed['path'];
$new_url .= '?';
$new_url .= implode('&', $args);
print $new_url;