Portably retrieve Subversion 1.7 revision in PHP? - php

In Subversion 1.6, there was an .svn directory in every working copy directory. I could use the following code to quickly retrive the current revision number without the need for shell access/execution.
public function getSubversionRevision() {
if(file_exists("../.svn/entries")) {
$svn = File("../.svn/entries");
return (int)$svn[3];
}
return false;
}
Subversion 1.7 breaks this code. There is now only one .svn directory per local repository. There is an entries file in this directory but it no longer has anything useful for me. It looks like everything I need is now in a SQLite database. Specifically wc.db. I suppose I could use PHP's SQLite functions to get the info I need, but this sounds a little too expensive to run on every (or close to every) page load.
Any ideas? Breaking out the exec function and hoping that Subversion binaries are installed (and in the $PATH!) is a last resort. To summarize, I need to find a way to locate the .svn directory at the root of the repository (which could be different depending on your checkout location) and then somehow parse a file in there (probably wc.db) in a cost-effective way.

try using a SVN library SVN as it will give you access to repository information and more functionality over SVN repository.
Take a look at function svn_status you will receive and array of svn repository information
Array (
[0] => Array (
[path] => /home/bob/wc/sandwich.txt
[text_status] => 8 // item was modified
[repos_text_status] => 1 // no information available, use update
[prop_status] => 3 // no changes
[repos_prop_status] => 1 // no information available, use update
[name] => sandwich.txt
[url] => http://www.example.com/svnroot/deli/trunk/sandwich.txt
[repos] => http://www.example.com/svnroot/
[revision] => 123 // <-- Current Revision
//..
)
)

You can write a script to svn update for you every time, and scrap the revision number from the update's output into a file. The following bash script should more or less do it:
#!/bin/bash
svn --non-interactive update ..
svn --non-interactive update .. | perl -p -e "s/.* revision ([\d]*)\./\$1/" > ../version.phtml;

I implemented a clumsy (but relatively thorough) implementation of Subversion 1.7 revision retrieval in TheHostingTool. Check the commit message for an explanation. The function is contained in class_main.php which is one directory after /trunk (/trunk/includes/class_main.php). You'll need to adjust relative paths for your specific needs. This is a slightly modified version of the function found in class_main.php for use elsewhere.
public function getSubversionRevision() {
// This will work for Subverson 1.6 clients and below
if(file_exists("../.svn/entries")) {
$svn = File("../.svn/entries");
return (int)$svn[3];
}
// Check the previous directories recursively looking for wc.db (Subversion 1.7)
$searchDepth = 3; // Max search depth
// Do we have PDO? And do we have the SQLite PDO driver?
if(!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
$searchDepth = 0; // Don't even bother...
}
for($i = 1; $i <= $searchDepth; $i++) {
$dotdot .= '../';
if(!file_exists("$dotdot.svn/wc.db")) {
continue;
}
$wcdb = new PDO("sqlite:$dotdot.svn/wc.db");
$result = $wcdb->query('SELECT "revision" FROM "NODES" WHERE "repos_path" = "'.basename(realpath('..')).'"');
return (int)$result->fetchColumn();
}
if($this->canRun('exec')) {
exec('svnversion ' . realpath('..'), $out, $return);
// For this to work, svnversion must be in your PHP's PATH enviroment variable
if($return === 0 && $out[0] != "Unversioned directory") {
return (int)$out[0];
}
}
return false;
}
Instead of .= '../' you may want to use str_repeat if you don't start at 1. Or, you could simply define $dotdot ahead of time to wherever you would like to start.

Or just query:
SELECT "changed_revision" FROM "NODES" ORDER BY changed_revision DESC LIMIT 1;
if you wonder the last revision of the whole repo! :)

Excuse me, but if you want portable and bullet-proof solution, why not call Subversion CLI inside PHP?
You can use svn info inside any directory of WC (capture, parse output and get a set of data) of svnversion if you want to get only global revision

Related

xhprof-html won't show Run Report (No XHProf runs specified in the URL)

How do I show the Run Report for a given xhprof run using xhprof-html?
I installed tideways-xhprof.
apt-get install php-dev
git clone "https://github.com/tideways/php-xhprof-extension.git"
cd php-xhprof-extension
phpize
./configure
make
make install
I enabled it in my php.ini
extension=/usr/lib/php/20190902/tideways_xhprof.so
I configured wordpress to write-out xhprof runs in my wp-config.php file
# PHP profiling with tideways-xhprof
# * https://pressjitsu.com/blog/profiling-wordpress-performance/
if ( isset( $_GET['profile'] ) && $_GET['profile'] === 'secret-string' ) {
tideways_xhprof_enable( TIDEWAYS_FLAGS_MEMORY + TIDEWAYS_FLAGS_CPU );
register_shutdown_function( function() {
$results = tideways_xhprof_disable();
file_put_contents( '/path/to/my/xhprof/dir/' .date('Y-m-d\TH:i:s\Z', time() - date('Z')). '.xhprof' , serialize( $results ) );
});
}
/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');
And I was successfully able to dump-out xhprof files by visiting https://example.com/index.php?profile=secret-string
root#host:/path/to/my/xhprof/dir/# du -sh *
1,6M 2022-05-25T18:15:45Z.xhprof
1,6M 2022-05-25T18:18:38Z.xhprof
root#host:/path/to/my/xhprof/dir/#
I also created a website with xhprof-html. And I configured it to find the above .xhprof files
root#host:/var/www/xhprof/xhprof-html# diff index.orig.php index.php
83c83
< $xhprof_runs_impl = new XHProfRuns_Default();
---
> $xhprof_runs_impl = new XHProfRuns_Default( '/path/to/my/xhprof/dir' );
root#host:/var/www/xhprof/xhprof-html#
Now I can load access the xhprof-html/index.php file and it successfully displays my two .xhprof files at https://xhprof.example.com/xhprof-html/index.php
No XHProf runs specified in the URL.
Existing runs:
2022-05-25T18:18:38Z.xhprof 2022-05-25 18:18:38
2022-05-25T18:15:45Z.xhprof 2022-05-25 18:15:45
And if I click on either one, I'm redirected (as expected) to either of these pages:
https://xhprof.example.com/xhprof-html/index.php?run=2022-05-25T18:18:38Z&source=xhprof
https://xhprof.example.com/xhprof-html/index.php?run=2022-05-25T18:15:45Z&source=xhprof
However, I would expect the above pages to render the actual Run Report. But they do not. Instead, I just see
No XHProf runs specified in the URL.
Existing runs:
2022-05-25T18:18:38Z.xhprof 2022-05-25 18:18:38
2022-05-25T18:15:45Z.xhprof 2022-05-25 18:15:45
Hello? The XHProf run is clearly specified in the URL.
How do I get xhprof-html to display the actual Run Report?
The Run Report is not shown because it can't read the file.
Problem
You are generating the file using the tideways xhprof and writing a serialized output to a file
https://github.com/tideways/php-xhprof-extension
$results = tideways_xhprof_disable();
file_put_contents( '/path/to/my/xhprof/dir/' .date('Y-m-d\TH:i:s\Z', time() - date('Z')). '.xhprof' , serialize( $results ) );
(source)
But then, you switch to hosting an older (no longer maintained) fork of xhprof to serve xhprof_html in your web server's document root
https://github.com/phacility/xhprof
Note: Of course you have to do this, and even the tideways docs link you to the phacility repo. The tideways repo itself curiously does not include the xhprof_html directory, possibly because tideways is a business that sells xhprof visualization SaaS -- so they stripped the "poor man's" xhprof_html from the fork of xhprof that they maintain
If you want to read the file using XHProfRuns_Default() then you should write the file using XHProfRuns_Default().
Solution
Change the block in your wp-config.php to the following
# PHP profiling with tideways-xhprof
# * https://pressjitsu.com/blog/profiling-wordpress-performance/
if ( isset( $_GET['profile'] ) && $_GET['profile'] === 'secret-string' ) {
tideways_xhprof_enable( TIDEWAYS_FLAGS_MEMORY + TIDEWAYS_FLAGS_CPU );
register_shutdown_function( function() {
$results = tideways_xhprof_disable();
include_once( '/var/www/xhprof/xhprof_lib/utils/xhprof_runs.php' );
$XHProfRuns = new XHProfRuns_Default( '/path/to/my/xhprof/dir' );
$XHProfRuns->save_run( $results, date('Y-m-d\TH:i:s\Z', time() - date('Z')) );
});
}
Then generate a new profile
https://example.com/index.php?profile=secret-string
You'll now see a new item named something like 62906fb2c49b4.2022-05-27T06:29:06Z.xhprof
No XHProf runs specified in the URL.
Existing runs:
62906fb2c49b4.2022-05-27T06:29:06Z.xhprof 2022-05-27 06:29:06
2022-05-25T18:18:38Z.xhprof 2022-05-25 18:18:38
2022-05-25T18:15:45Z.xhprof 2022-05-25 18:15:45
Click this new item, and you'll acctually see the Run Report now that it can read the xhprof file format.
https://xhprof.example.com/xhprof-html/index.php?run=62906fb2c49b4&source=2022-05-27T06:29:06Z
For more info on this issue, see:
https://tech.michaelaltfield.net/2022/06/07/wordpress-xhprof/

How to validate DNSSEC in PHP?

I've been trying to figure out a way to validate DNS records in PHP (oa1) but have come up short.
I can validate a whole domain with this library, but not the individual records: https://github.com/metaregistrar/php-dnssec-validator
In addition, that library only allows for a very small set of TLDs to be validated.
Is there another library out there that can handle this for me, or perhaps something else I should look into?
I've also found this: http://www.phpclasses.org/package/9031-PHP-Validate-DNSSEC-keys-and-calculate-the-DS-record.html
But I have no idea how to get the keys to use in their validating function.
Help please!
UPDATE
So, I ended up using this...
exec('host -t RRSIG ' . $domain, $output);
Returns the RRSIG, or lack thereof, with minimal hassle.
The PHP engine has a fixed set of DNS record types it supports, all defined by the type parameter to dns_get_record. You can double check this list by looking in the engine code that implements DNS queries.
Unfortunately, none of the DNSSEC records are in that pre-defined list. So, you need to rely on a library or an external tool.
I'd use Net_DNS2, as it supports many DNSSEC RR. Example:
$google = new \Net_DNS2_Resolver(['nameservers' => ['8.8.8.8', '8.8.4.4']]);
$google->dnssec = true;
try {
$result = $google->query('kyhwana.org', 'SSHFP');
} catch(\Net_DNS2_Exception $ex) {
die($ex->getMessage());
}
foreach ($result->answer as $answer) {
if ($answer instanceof \Net_DNS2_RR_SSHFP) {
printf(
'%s %d %s %s %d %d %s' . PHP_EOL,
$answer->name,
$answer->ttl,
$answer->class,
$answer->type,
$answer->algorithm,
$answer->fp_type,
$answer->fingerprint
);
} else if ($answer instanceof \Net_DNS2_RR_RRSIG) {
printf('Signed by %s: %s' . PHP_EOL, $answer->signname, $answer->signature);
}
}
Aside: if your domain uses ECDSA algorithm or the SHA-256 fingerprint (like the example above), then you need the latest Net_DNS2 code which fixes Issue #39.
I haven't implemented the code yet but it looks to me like using php to verify the signatures themselves is a mistake. Use a local recursive resolver like unbound on the local server that enforces DNSSEC.
Then in php with the PEAR module Net_DNS2 look for an RRSIG record for the domain of interest. The result should tell you the zone responsible for that record.
Look for a DNSKEY record in that zone. If present, then look for a DS record.
The DS record will come from the parent zone (e.g. .email for deviant.email) and if present indicates DNSSEC is present for the zone.
If DNSSEC is present for the zone, then all the results for the zone are valid if your local recursive nameserver enforces DNSSEC.
I believe with Net_DNS2 that using
r = new Net_DNS2_Resolver(array('nameservers' => array('127.0.0,1')));
r->dnssec = true;
will also result in Net_DNS2 also validating the results if DNSSEC is active - which is indicated by the DS record that comes from the parent zone. But using a recursive resolver (preferably on the webserver) that enforces DNSSEC is probably safest.
Make sure the recursive resolver isn't listening on public interfaces so it doesn't get used in reflective amplification attacks.
Having the same goal (validate DNSSEC in PHP), i ended up with:
exec("drill -k root.keys -TDQ $domain SOA | grep -v '^;;'", $output);
This is doing a complete topdown DNSSEC validation.
You need root keys you can retrieve with:
unbound-anchor # NB: on FreeBSD, use local-unbound-anchor for "base" version
Or "manually":
dig . dnskey > root.keys
To confirm that the record is fully DNSSEC validated, check the last line of exec output:
[S] self sig OK This is not satisfying, probably zone key has not been populated in parent zone
[B] bogus
[T] trusted This means that everything is OK!

Magento - API not being updated

The previous provider killed several kittens by changing the core many times of a Magento 1.6.2.0 distribution.
So, in order to solve an issue in record time, we had to screw and hit the kittens' corspses: we still maintain the modified API: catalog_category.assignProduct.
So far, we have this API for the method:
public function assignProduct($categoryId, $productId, $position = null, $identifierType = null)
{
$datos = explode('|', $categoryId);
$productId = array_shift($datos);
$categorias_actuales = Mage::getModel('catalog/category')
->getCollection()
->addIsActiveFilter()
->addIdFilter($datos)
->addLevelFilter(3)
->getAllIds();
$incat = function($cat) use ($categorias_actuales) {
return in_array($cat, $categorias_actuales);
};
$result = array_combine($datos, array_map($incat, $datos));
try {
$product = Mage::helper('catalog/product')->getProduct($productId, null, $identifierType);
$product->setCategoryIds($categorias_actuales);
$product->save();
} catch (Mage_Core_Exception $e) {
$this->_fault('data_invalid', $e->getMessage());
} catch (Exception $e) {
$this->_fault('internal_error', $e->getMessage());
}
return $result;
}
The intention of the API was to assign many categories at once for a product. However this is a modified version because the previous was prohibitively slow. And the curious fact is that this worked until recently. It's intention was to:
only the first parameter is used, and instead of an integer value, a kitten was killed here and the API now expects a string like "productId|catgId1|catgId2|cargId3|..." where the first value is a product id and the second to last values are categories ids; each of them being integer values. The parameter is broken by pipe character ("|") and the product id is popped. In this way, we have the product id in one var, and the categories array in another var.
get the categories collection keeping only the active ones in level 3 (i.e. depth for the category in the tree) whose ids are among the specified ones. This means: if provided categories are array(3, 5, 7, 8) (e.g. from a "245|3|5|7|8" parameter, being "245" the product id) and one of them does not exist or is not active (e.g. 7 is not active and 8 does not exist), the returned value in $categorias_actuales is [3, 5].
as for debugging porpuse, map each input category to existence and validity. this means: existent&&active&&level=3 categories will have "true" as value, while non-existent or non-active categories will have "false" as value. For the given example, the returned array would be: array(3 => true, 5 => true, 7 => false, 8 => false), since 7 is not active and 8 does not exist.
in the try-catch block: a. retrieve the product; b. set the filtered ids (in the example: [3, 5]) as product categories in the just-fetched product; c. save the product.
However, I have the following issues:
The function returns true, so returning $result would not give me the array $results as return value (my intention was to know in which categories was the product actually inserted).
Changing to false instead of $result in the return statement had no effect at all: the obtained/displayed value from the API call was still true.
Throwing a new Exception("something's going on here") had no effect at all. Still true at output.
dying (die('something's going on here')) neither had effect. Still seeing (guess what?) true at the output.
Edit: Also tried a syntax error!! (guess what? nothing happens).
It's not only that I tried these steps, but also refreshed the cache (System > Cache Management > select all and refresh, and even clicking the "Flush Magento Cache" button).
Questions:
1. given the issues before: how can I debug that API call?
2. what could be causing the product not being saved with their categories? By calling category.level I can see that the given ids (i.e. given in the first parameter to category.assignProduct) exist.
I'm a n00b # magento API and perhaps I'm missing something usually obvious. Any help would be appreciated.
Did you disable compilation? You can do it in one of these two ways.
System -> Tools -> Compilation in admin
Use SSH/shell and navigate to your magento root. Do the following commands:
cd shell
php -f compiler.php disable
Also, if your web server runs PHP APC with apc.stat set to 0 (you can check this by running php -i | grep apc.stat or looking for it at any phpinfo() output), please restart your apache2 web server or if you use php5-fpm -- restart that one, too.
Then, if all else fails, try looking for an equivalent file at app/code/local.
e.g. if your file is somewhere in app/code/core/Mage/Path/ToApiFile.php, look for the local version at app/code/local/Mage/Path/ToApiFile.php.
Finally, a very useful shell command for searching code instances:
$ cd app/code/
$ grep -rin "function assignProduct" *

Rackspace - php-opencloud filters - Documentation for valid ObjectList filters?

Anyone know if/where there is documentation for valid ObjectList filter arrays?
The project's entry on github has a tiny blurb on it directing me to the API documentation, but that also fails to have a comprehensive list, and a search on 'filters' talks about containers only, not the object themselves.
I have a list of videos, each in four different formats named the same thing (sans filetype). Using the php-opencloud API, I want to GET only one of those video formats (to grab the unique filename rather than all its different formats).
I figured using a filter is the way to go, but I can't find any solid documentation.
Someone has got to have done this before. Help a noob out?
Most of the links on this page are dead now. Here's a current link to the php-opencloud documentation, which includes an example of using a prefix to filter the objectList results:
http://docs.php-opencloud.com/en/latest/services/object-store/objects.html#list-objects-in-a-container
I didn't find documentation of this, but apparently when the Rackspace Cloud Files documentation mentions arguments in a query string, those translate to arguments in an objectList method call like this:
GET /v1/MossoCloudFS_0672d7fa-9f85-4a81-a3ab-adb66a880123/AppleType?limit=2&marker=grannysmith
equals
$container->objectList(array('limit'=>'2', 'marker'=>'grannysmith'));
As Glen's pointed out, there isn't support (at the moment) for the service to apply filters on objects. The only thing which you might be interested in is supplying a prefix, which allows you to refine the objects returned based on how the filenames start. So if you sent 'bobcatscuddling' as the prefix, you'd get back all associated video formats for that one recording.
Your only option, it seems, is to get back all objects and iterate over the collection:
use OpenCloud\Rackspace;
$connection = new Rackspace(RACKSPACE_US, array(
'username' => 'foo',
'apiKey' => 'bar'
));
$service = $connection->objectStore('cloudFiles', 'DFW', 'publicURL');
$container = $service->container('CONTAINER_NAME');
$processedObjects = array();
$marker = '';
while ($marker !== null) {
$objects = $container->objectList('marker' => $marker);
$total = $objects->count();
$count = 0;
while ($object = $objects->next()) {
// Extract the filename
$filename = pathinfo($object->name, PATHINFO_FILENAME);
// Make sure you only deal with the filename once (i.e. to ignore different extensions)
if (!in_array($processedObjects, $filename)) {
// You can do your DB check here...
// Stock the array
$processedObjects[] = $filename;
}
$count++;
$marker = ($count == $total) ? $object->name : null;
}
}
What you'll notice is that you're incrementing the marker and making a new request for each 10,000 objects. I haven't tested this, but it'll probably lead you in the right direction.
Unfortunately, the underlying API doesn't support filtering for objects in Swift/Cloud Files containers (cf. http://docs.rackspace.com/files/api/v1/cf-devguide/content/List_Objects-d1e1284.html). The $filter parameter is supported as part of the shared code, but it doesn't actually do anything with Cloud Files here.
I'll see if I can get the docs updated to reflect that.

Anyway to do MongoDB Sharding in PHP?

I have started using MongoDB for one of my PHP project. In that database, i am trying to use MongoDB sharding concept. I got the below link and tried,
MongoDB Sharding Example
It is working well. But the problem is, in the above example, everything is done in command prompt. But i am trying to do everything in PHP. I am not able to get any example in PHP.
So far, i have started with these piece of codes,
$connection = new Mongo();
$db = $connection->selectDB('TestDB');
$db = $connection->TestDB;
$connection->selectDB('admin')->command(array('addshard'=>'host:port'));
$connection->selectDB('admin')->command(array('enablesharding'=>'TestDB'));
$connection->selectDB('admin')->command(array('shardcollection'=>'TestDB.large', 'key' => '_id'));
It is not working. Also, i dont know how to set shard servers and config database in PHP.
Is there any other way to do MongoDB sharding in PHP?
MongoDB's database commands are case-sensitive. You were passing in lowercase command names, while the real commands are camelCased (see: sharding commands). Additionally, the key paremeter of shardCollection needs to be an object.
A corrected version of your above code would look like:
$connection->selectDB('admin')->command(array('addShard'=>'host:port'));
$connection->selectDB('admin')->command(array('enableSharding'=>'TestDB'));
$connection->selectDB('admin')->command(array('shardCollection'=>'TestDB.large', 'key' => array('_id' => 1)));
Additionally, you should have been able to examine the command result of to determine why this wasn't working. The ok field would be zero, and an errmsg field would explain the error (e.g. "no such cmd: addshard").
Lastly, if you're attempting to mimic the shell's functionality to configure sharding in PHP, you may realize that some of the shell methods are more than just database commands. For any of those methods, you can get the JS source by omitting the parentheses. For example:
> sh.addTagRange
function ( ns, min, max, tag ) {
var config = db.getSisterDB( "config" );
config.tags.update( {_id: { ns : ns , min : min } } ,
{_id: { ns : ns , min : min }, ns : ns , min : min , max : max , tag : tag } ,
true );
sh._checkLastError( config );
}
That would be a useful hint for what an addTagRange() function in PHP would need to do.

Categories