Is there a better or more robust solution to this problem than the one I have found?
$string = "a:1:{s:19:\"is_featured_service\";b:0;}";
$unserialized_string = #unserialize($string);
if ($unserialized_string === false){
$unserialized_string = 'another value';
}
I like this because you don't have to try to supress errors:
/**
* Check value to find if it is serialized data.
*
* Function borrowed from Wordpress.
*
* #param mixed $data Value to check to see if was serialized.
* #return bool False if not serialized and true if it was.
*/
function is_serialized( $data ) {
// 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;
$lastc = $data[$length-1];
if ( ';' !== $lastc && '}' !== $lastc )
return false;
$token = $data[0];
switch ( $token ) {
case 's' :
if ( '"' !== $data[$length-2] )
return false;
case 'a' :
case 'O' :
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b' :
case 'i' :
case 'd' :
return (bool) preg_match( "/^{$token}:[0-9.E-]+;\$/", $data );
}
return false;
}
$string = "a:1:{s:19:\"is_featured_service\";b:0;}";
$x = is_serialized( $string )
? unserialize( $string )
: 'Some default value';
I would only keep the # if you dont want to deal with errors, which it seems like you do. Then change it into ternary to make it smaller:
$unserialized_string = #unserialize($string) ?: 'another value';
Related
I've discovered that the PHP \Datetime class returns "now" values for some odd inputs. I've already seen the similar question at DateTime constructor in php -- which explains inputs such as single letters of the alphabet (they're military time zones). But I've discovered some new oddballs that I would expect to cause an error, not return a value. Such as...
new \Datetime( '.' )
new \Datetime( ',' )
Can anyone explain WHY these don't cause errors, and can anyone tell me what other odd values I should expect to return valid dates? Is this a bug in PHP?
(Yes, I've already noticed 0 and basically anything you find in timezone_abbreviations_list() )
UPDATE:
I thought I'd share my "turn various inputs into PHP Datetime object" function with you all. Originally done as a conditional "whatever I'm handed, output an object", but thanks to #Syscall's input I've been able to harden it a bit against spurious inputs that inappropriately return "now" datetimes.
I could crank this down even harder against various timezone strings, but I don't think it's necessary for my usage.
/**
* If $input is already a DateTime object, leave it alone. Otherwise convert to a DateTime object
* If $immutable = true, converts strings OR DateTime to DateTimeImmutable
* Can be used to convert DateTimeInterface objects to and from immutable
*
* #param string|\DatetimeInterface $input
* #param bool $immutable
* #return \DateTime|\DateTimeImmutable|\DateTimeInterface|null
*/
function ensureDateTime ( $input, $immutable = NULL ) {
if ( ! $input instanceof \DateTimeInterface ) {
$output = NULL;
if( is_string( $input ) || ! $input ) {
$trimmed = trim( $input, ".,\n\0\t " ); // Thanks https://stackoverflow.com/a/48956505/339440
$ignore = ( $trimmed == '' && $trimmed != $input )
|| in_array( $trimmed, ['0000-00-00', '0000-00-00 00:00:00'], true )
|| ( strlen( $trimmed ) == 1 && preg_match( '#[a-zA-Z]#', $trimmed ) == 1 );
if ( ! $ignore ) {
try {
$input = trim( $input );
if ( $immutable ) {
$output = new \DateTimeImmutable( $input );
} else {
$output = new \DateTime( $input );
}
} catch( \Exception $e ) {
// suppress DateTime::__construct() errors. $output remains NULL
}
}
}
} elseif ( true === $immutable && $input instanceof \DateTime ) {
$output = new \DateTimeImmutable( $input->format(TIMESTAMPFORMAT), $input->getTimezone() );
} elseif ( false === $immutable && $input instanceof \DateTimeImmutable ) {
$output = new \DateTime( $input->format(TIMESTAMPFORMAT), $input->getTimezone() );
} else {
$output = $input;
}
return $output;
}
Values ".,\n\0\t " are trimmed :
See /php-7.2.2/ext/date/lib/parse_date.c, static int scan() :
switch (yych) {
case 0x00:
case '\n': goto yy51;
case '\t':
case ' ': goto yy48;
case ',':
case '.': goto yy50;
Other characters like (+-#, letters, numbers, requires other validation.
Example :
new Datetime("\0 \n\t,."); // works
But :
new Datetime('#') ; // fails
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?
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;
}
I've been trying to build this recursive function for the better part of a day now, but I just can't seem to get it to work the way I want.
First, I have a property which holds some data that the function have to access:
$this->data
And then I have this string which the intention is to turn into a relative path:
$path = 'path.to.%id%-%folder%.containing.%info%';
The part of the string that are like this: %value% will load some dynamic values found in the $this->data property (like so: $this->data['id']; or $this->data['folder'];
and to make things really interesting, the property can reference itself again like so: $this->data['folder'] = 'foldername.%subfolder%'; and also have two %values% separated by a - that would have to be left alone.
So to the problem, I've been trying to make a recursive function that will load the dynamic values from the data property, and then again if the new value contains another %value% and so on until no more %value%'s are loaded.
So far, this is what I've been able to come up with:
public function recursiveFolder( $folder, $pathArr = null )
{
$newPathArr = explode( '.', $folder );
if ( count ( $newPathArr ) !== 1 )
{
foreach( $newPathArr as $id => $folder )
{
$value = $this->recursiveFolder( $folder, $newPathArr );
$resultArr = explode( '.', $value );
if ( count ( $resultArr ) !== 1 )
{
foreach ( $resultArr as $nid => $result )
{
$nvalue = $this->recursiveFolder( $result, $newPathArr );
$resultArr[$nid] = $nvalue;
}
}
$resultArr = implode( '.',$resultArr );
$newPathArr[$id] = $resultArr;
}
}
else
{
$pattern = '/%(.*?)%/si';
preg_match_all( $pattern, $folder, $matches );
if ( empty( $matches[0] ) )
{
return $folder;
}
foreach ( $matches[1] as $mid => $match )
{
if ( isset( $this->data[$match] ) && $this->data[$match] != '' )
{
$folder = str_replace( $matches[0][$mid], $this->data[$match], $folder );
return $folder;
}
}
}
return $newPathArr;
}
Unfortunately it is not a recursive function at all as it grinds to a halt when it has multiple layers of %values%, but works with two layers -barely-. (I just coded it so that it would work at a bare minimalistic level this point).
Here's how it should work:
It should turn:
'files.%folder%.blog-%type%.and.%time%'
into:
'files.foldername.blog-post.and.2013.feb-12th.09'
based on this:
$data['folder'] = 'foldername';
$data['type'] = 'post';
$data['time'] = '%year%.%month%-%day%';
$data['year'] = 2013;
$data['month'] = 'feb';
$data['day'] = '12th.%hour%';
$data['hour'] = '09';
Hope you can help!
Jay
I don't see the need for this too be solved recursively:
<?php
function putData($str, $data)
{
// Repeat the replacing process until no more matches are found:
while (preg_match("/%(.*?)%/si", $str, $matches))
{
// Use $matches to make your replaces
}
return $str;
}
?>
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.