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?
Related
Some data in my MySQL database is stored in serialized format, e.g. a:1:{s:3:"url";s:70:"http://www.myurl.com/wp-content/uploads/2014/01/Crash_Test_Dummy-1.jpg";}
other data in the same type of field is stored unserialized, eg. http://www.myurl.com/wp-content/uploads/2014/01/Crash_Test_Dummy-1.jpg.
Now when I try to retrieve data with $var=unserialize($data); with $data being the above unserialized string, I get an error Notice: unserialize(): Error at offset 0 of 69 bytes in ...
Is there a quick way to serialize all unserialized fields in my DB? Alternatively, is there a way to tell the server unserialize($data) if $data is serialized?
Thank you!
WordPress likes to complicate things a bit when it comes to store information on db...
If you are developing code inside WP, just use maybe_unserialize
Otherwise, you can copy that function form wp-includes/functions.php
/**
* Unserialize value only if it was serialized.
*
* #since 2.0.0
*
* #param string $original Maybe unserialized original, if is needed.
* #return mixed Unserialized data can be any type.
*/
function maybe_unserialize( $original ) {
if ( is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in
return #unserialize( $original );
return $original;
}
/**
* Check value to find if it was serialized.
*
* If $data is not an string, then returned value will always be false.
* Serialized data is always a string.
*
* #since 2.0.5
*
* #param mixed $data Value to check to see if was serialized.
* #param bool $strict Optional. Whether to be strict about the end of the string. Defaults true.
* #return bool False if not serialized and true if it was.
*/
function is_serialized( $data, $strict = true ) {
// if it isn't a string, it isn't serialized
if ( ! is_string( $data ) )
return false;
$data = trim( $data );
if ( 'N;' == $data )
return true;
$length = strlen( $data );
if ( $length < 4 )
return false;
if ( ':' !== $data[1] )
return false;
if ( $strict ) {
$lastc = $data[ $length - 1 ];
if ( ';' !== $lastc && '}' !== $lastc )
return false;
} else {
$semicolon = strpos( $data, ';' );
$brace = strpos( $data, '}' );
// Either ; or } must exist.
if ( false === $semicolon && false === $brace )
return false;
// But neither must be in the first X characters.
if ( false !== $semicolon && $semicolon < 3 )
return false;
if ( false !== $brace && $brace < 4 )
return false;
}
$token = $data[0];
switch ( $token ) {
case 's' :
if ( $strict ) {
if ( '"' !== $data[ $length - 2 ] )
return false;
} elseif ( false === strpos( $data, '"' ) ) {
return false;
}
// or else fall through
case 'a' :
case 'O' :
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b' :
case 'i' :
case 'd' :
$end = $strict ? '$' : '';
return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
}
return false;
}
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.
I have created a script that generates information about a torrent file! But I'm lacking in creating a seeds and peers displaying function! Someone told me that they are in the completed field defined in the torrent. Please my class function codes from which I display the generated information using a bencode.php which takes out the data and this script ,named torrent.php converts it in readable form!
<?php
include_once('bencode.php');
class Torrent
{
// Private class members
private $torrent;
private $info;
// Public error message, $error is set if load() returns false
public $error;
// Load torrent file data
// $data - raw torrent file contents
public function load( &$data )
{
$this->torrent = BEncode::decode( $data );
if ( $this->torrent->get_type() == 'error' )
{
$this->error = $this->torrent->get_plain();
return false;
}
else if ( $this->torrent->get_type() != 'dictionary' )
{
$this->error = 'The file was not a valid torrent file.';
return false;
}
$this->info = $this->torrent->get_value('info');
if ( !$this->info )
{
$this->error = 'Could not find info dictionary.';
return false;
}
return true;
}
// Get comment
// return - string
public function getComment() {
return $this->torrent->get_value('comment') ? $this->torrent->get_value('comment')->get_plain() : null;
}
// Get creatuion date
// return - php date
public function getCreationDate() {
return $this->torrent->get_value('creation date') ? $this->torrent->get_value('creation date')->get_plain() : null;
}
// Get created by
// return - string
public function getCreatedBy() {
return $this->torrent->get_value('created by') ? $this->torrent->get_value('created by')->get_plain() : null;
}
// Get name
// return - filename (single file torrent)
// directory (multi-file torrent)
// see also - getFiles()
public function getName() {
return $this->info->get_value('name')->get_plain();
}
// Get piece length
// return - int
public function getPieceLength() {
return $this->info->get_value('piece length')->get_plain();
}
// Get pieces
// return - raw binary of peice hashes
public function getPieces() {
return $this->info->get_value('pieces')->get_plain();
}
// Get private flag
// return - -1 public, implicit
// 0 public, explicit
// 1 private
public function getPrivate() {
if ( $this->info->get_value('private') )
{
return $this->info->get_value('private')->get_plain();
}
return -1;
}
// Get a list of files
// return - array of Torrent_File
public function getFiles() {
// Load files
$filelist = array();
$length = $this->info->get_value('length');
if ( $length )
{
$file = new Torrent_File();
$file->name = $this->info->get_value('name')->get_plain();
$file->length = $this->info->get_value('length')->get_plain();
array_push( $filelist, $file );
}
else if ( $this->info->get_value('files') )
{
$files = $this->info->get_value('files')->get_plain();
while ( list( $key, $value ) = each( $files ) )
{
$file = new Torrent_File();
$path = $value->get_value('path')->get_plain();
while ( list( $key, $value2 ) = each( $path ) )
{
$file->name .= "/" . $value2->get_plain();
}
$file->name = ltrim( $file->name, '/' );
$file->length = $value->get_value('length')->get_plain();
array_push( $filelist, $file );
}
}
return $filelist;
}
// Get a list of trackers
// return - array of strings
public function getTrackers() {
// Load tracker list
$trackerlist = array();
if ( $this->torrent->get_value('announce-list') )
{
$trackers = $this->torrent->get_value('announce-list')->get_plain();
while ( list( $key, $value ) = each( $trackers ) )
{
if ( is_array( $value->get_plain() ) ) {
while ( list( $key, $value2 ) = each( $value ) )
{
while ( list( $key, $value3 ) = each( $value2 ) )
{
array_push( $trackerlist, $value3->get_plain() );
}
}
} else {
array_push( $trackerlist, $value->get_plain() );
}
}
}
else if ( $this->torrent->get_value('announce') )
{
array_push( $trackerlist, $this->torrent->get_value('announce')->get_plain() );
}
return $trackerlist;
}
// Helper function to make adding a tracker easier
// $tracker_url - string
public function addTracker( $tracker_url )
{
$trackers = $this->getTrackers();
$trackers[] = $tracker_url;
$this->setTrackers( $trackers );
}
// Replace the current trackers with the supplied list
// $trackerlist - array of strings
public function setTrackers( $trackerlist )
{
if ( count( $trackerlist ) >= 1 )
{
$this->torrent->remove('announce-list');
$string = new BEncode_String( $trackerlist[0] );
$this->torrent->set( 'announce', $string );
}
if ( count( $trackerlist ) > 1 )
{
$list = new BEncode_List();
while ( list( $key, $value ) = each( $trackerlist ) )
{
$list2 = new BEncode_List();
$string = new BEncode_String( $value );
$list2->add( $string );
$list->add( $list2 );
}
$this->torrent->set( 'announce-list', $list );
}
}
// Update the list of files
// $filelist - array of Torrent_File
public function setFiles( $filelist )
{
// Load files
$length = $this->info->get_value('length');
if ( $length )
{
$filelist[0] = str_replace( '\\', '/', $filelist[0] );
$string = new BEncode_String( $filelist[0] );
$this->info->set( 'name', $string );
}
else if ( $this->info->get_value('files') )
{
$files = $this->info->get_value('files')->get_plain();
for ( $i = 0; $i < count( $files ); ++$i )
{
$file_parts = split( '/', $filelist[$i] );
$path = new BEncode_List();
foreach ( $file_parts as $part )
{
$string = new BEncode_String( $part );
$path->add( $string );
}
$files[$i]->set( 'path', $path );
}
}
}
// Set the comment field
// $value - string
public function setComment( $value )
{
$type = 'comment';
$key = $this->torrent->get_value( $type );
if ( $value == '' ) {
$this->torrent->remove( $type );
} elseif ( $key ) {
$key->set( $value );
} else {
$string = new BEncode_String( $value );
$this->torrent->set( $type, $string );
}
}
// Set the created by field
// $value - string
public function setCreatedBy( $value )
{
$type = 'created by';
$key = $this->torrent->get_value( $type );
if ( $value == '' ) {
$this->torrent->remove( $type );
} elseif ( $key ) {
$key->set( $value );
} else {
$string = new BEncode_String( $value );
$this->torrent->set( $type, $string );
}
}
// Set the creation date
// $value - php date
public function setCreationDate( $value )
{
$type = 'creation date';
$key = $this->torrent->get_value( $type );
if ( $value == '' ) {
$this->torrent->remove( $type );
} elseif ( $key ) {
$key->set( $value );
} else {
$int = new BEncode_Int( $value );
$this->torrent->set( $type, $int );
}
}
// Change the private flag
// $value - -1 public, implicit
// 0 public, explicit
// 1 private
public function setPrivate( $value )
{
if ( $value == -1 ) {
$this->info->remove( 'private' );
} else {
$int = new BEncode_Int( $value );
$this->info->set( 'private', $int );
}
}
// Bencode the torrent
public function bencode()
{
return $this->torrent->encode();
}
// Return the torrent's hash
public function getHash()
{
return strtoupper( sha1( $this->info->encode() ) );
}
}
// Simple class to encapsulate filename and length
class Torrent_File
{
public $name;
public $length;
}
?>
Please help me out!
Thanks in advance!
Little late but the class you say to have created comes from:
https://github.com/torrage/Torrage
It's original purpose was not intended to retrieve that kind of data.
A class that gets you seeds and peers for torrent inclusive the rest of the data see:
https://github.com/adriengibrat/torrent-rw
That information's not stored in the .torrent file. It's highly dynamic data, which can change every microsecond on a 'busy' torrent. The server's not going to build a custom .torrent file with up-to-the-minute statistics every time someone downloads it.
Think about it for a second. You download a .torrent file on Monday, but only look at it next Friday. The stats are now a week old and stale.
You can, however, take the tracker information in the .torrent and query those trackers for the stats.
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;
I am trying to remove an element from a multidimentional array in PHP. Here is the code :
<?php
$tset = "ST3";
$gettc = "gettingtc1";
$getbid = "gettingbid1";
$getresultsid = "gettingresid1";
$users[$tset] = array();
$users[$tset][] = array( "testcase"=>"$gettc",
"buildid"=>"$getbid",
"resultsid"=>"$getresultsid"
);
$tset = "TEJ";
$gettc = "ggettingtc2";
$getbid = "gettingbid2";
$getresultsid = "gettingresid2";
$users[$tset][] = array( "testcase"=>"$gettc",
"buildid"=>"$getbid",
"resultsid"=>"$getresultsid"
);
$tset = "ST3";
$gettc = "ggettingtc12";
$getbid = "gettingbid13";
$getresultsid = "gettigresid14";
$users[$tset][] = array( "testcase"=>"$gettc",
"buildid"=>"$getbid",
"resultsid"=>"$getresultsid"
);
foreach ($users as $val => $yy)
{
echo "For $val the array is :";
foreach($yy as $uy)
{
echo $uy['testcase'].$uy['buildid'].$uy['resultsid'];
}
echo '<br>';
}
$ser = "gettingresid1";
$to = array_searchRecursive($ser,$users);
if($to <> 0)
{
print_r($to);
}
else
{
echo "not";
}
function array_searchRecursive( $needle, $haystack, $strict=true, $path=array() )
{
if( !is_array($haystack) ) {
return false;
}
foreach( $haystack as $key => $val ) {
if( is_array($val) && $subPath = array_searchRecursive($needle, $val, $strict, $path) ) {
$path = array_merge($path, array($key), $subPath);
return $path;
} elseif( (!$strict && $val == $needle) || ($strict && $val === $needle) ) {
$path[] = $key;
return $path;
}
}
return false;
}
?>
Where I am stuck is : $to holds the array that has my search element. But the results $to is holding should be removed from the original array $users.
Any help.
Thanks.
I think you want to use unset()
//assuming $to contains the key in $users that needs to be removed
//from the array
unset($users[$to]);
But as $to contains an array of keys to the element rather than a single key to the element, you would need to write your own functions to do what you want.
The function that does what you want is below, and will remove the element of the given array which has the address in $keys.
/**
* Removes the element with the keys in the array $keys
*
* #param haystack the array that contains the keys and values
* #param keys the array of keys that define the element to remove
* #return the new array
*/
function array_remove_bykeys( array $haystack, array $keys ){
//check if element couldn't be found
if ( empty($keys) ){
return $haystack;
}
$key = array_shift($keys);
if ( is_array($haystack[$key]) ){
$haystack[$key] = array_remove_bykeys($haystack[$key], $keys);
}else if ( isset($haystack[$key]) ){
unset($haystack[$key]);
}
return $haystack;
}
The other method would be to delete all the keys with the value you were looking for.
/**
* Removes all elements from that array that has the value $needle
* #param $haystack the origanal array
* #param $needle the value to search for
* #return a new array with the value removed
*/
function array_remove_recursive( array $haystack, $needle ){
foreach( $haystack as $key => $value ) {
if( is_array($value) ) {
$haystack[$key] = array_remove_recursive($value, $needle);
} else if ( $value === $needle ){
unset($haystack[$key]);
}
}
return $haystack;
}
For completeness (Although defiantly not recommended), here is a eval version:
$keys = array(...);//an array of keys
//$arr is the variable name that contains the value that you want to remove
eval ('unset($arr[\''.implode('\'][\'', $keys).'\']);');
Pass $users to array_searchRecursive by reference (add '&' to $haystack):
function array_searchRecursive( $needle, &$haystack, $strict=true, $path=array()
and then in array_searchRecursive, just before each return statement:
unset($haystack[$key]);