{"id":1627,"date":"2014-01-21T20:47:48","date_gmt":"2014-01-21T14:47:48","guid":{"rendered":"http:\/\/notes.norfolks.org\/?p=1627"},"modified":"2014-01-21T20:47:48","modified_gmt":"2014-01-21T14:47:48","slug":"getting-information-from-svn-with-php","status":"publish","type":"post","link":"https:\/\/notes.norfolks.fun\/?p=1627","title":{"rendered":"Getting information from SVN with PHP"},"content":{"rendered":"<p><strong>Fabien Potencier<\/strong><br \/>\n<em>February 03, 2009<\/em><\/p>\n<p>Last year, I deployed a new tool to manage symfony plugins. The first goal of this tool was to simplify the process of contributing new plugins. It proved to be very successful, and I had a good feeling that the plugin creation rate was rising since then. And yesterday, I wanted to have hard numbers to back me up.<\/p>\n<p>The question I wanted to answer was quite simple:<\/p>\n<p><em>How many plugins were created per month before and after the change?<\/em><\/p>\n<p>As the symfony project provides free SVN hosting for community plugins, most plugins are actually hosted on the official symfony Subversion repository. And thankfully, the svn command line can give you a lot of information about your project in a very friendly format to parse: XML.<\/p>\n<p>The first step is to get the log file as an XML file:<\/p>\n<pre lang=\"bash\">$ svn log --xml --verbose http:\/\/svn.symfony-project.com\/plugins > svn_log.xml<\/pre>\n<p>The output is similar to something like this:<\/p>\n<pre lang=\"xml\"><?xml version=\"1.0\"?>\r\n<log>\r\n  <logentry revision=\"1949\">\r\n    <author>fabien<\/author>\r\n    <date>2006-09-05T14:40:20.603942Z<\/date>\r\n    <paths>\r\n      <path action=\"A\">\/plugins\/sfGuardPlugin<\/path>\r\n    <\/paths>\r\n    <msg>added sfGuardPlugin<\/msg>\r\n  <\/logentry>\r\n \r\n  <!-- many more entries -->\r\n<\/log><\/pre>\n<p>This XML file contains everything I need: the date of each commit and the paths that were modified, added, or deleted.<\/p>\n<p>Reading an XML file with PHP is insanely easy thanks to simplexml:<\/p>\n<pre lang=\"php\">$xml = simplexml_load_file(dirname(__FILE__).'\/svn_log.xml');<\/pre>\n<p>The simplexml_load_file() function reads an XML file and returns an object that can manipulated to access the various information contained in the XML.<\/p>\n<p>Iterating over the logentry and extracting data is also simple enough:<\/p>\n<pre lang=\"php\">foreach ($xml->logentry as $logentry)\r\n{\r\n  $date = (string) $logentry->date;\r\n \r\n  foreach ($logentry->paths as $paths)\r\n  {\r\n    foreach ($paths->path as $path)\r\n    {\r\n      $action = $path['action'];\r\n      $content = (string) $path;\r\n    }\r\n  }\r\n}\r\n <\/pre>\n<p>Each tag is accessible as a property of the object and returns an object representing the XML child elements:<\/p>\n<pre lang=\"xml\">$xml->logentry;<\/pre>\n<p>As SimpleXML returns objects, we sometimes need to cast them to string:<\/p>\n<pre lang=\"xml\">$date = (string) $logentry->date;<\/pre>\n<p>Getting a tag attribute is as simple as using the object as an array:<\/p>\n<pre lang=\"xml\">$action = $path['action'];<\/pre>\n<p>Eventually, getting the content of a tag can be done by casting the object itself to a string:<\/p>\n<pre lang=\"xml\">$content = (string) $path;<\/pre>\n<p>Without further ado, the following code parses the XML file and detect plugin creation by checking if the path has been created (A) and if the path is of the form \/plugins\/XXXPlugin (a symfony plugin name always ends with Plugin).<\/p>\n<pre lang=\"php\">$numbers = array();\r\n \r\n$xml = simplexml_load_file(dirname(__FILE__).'\/svn_log.xml');\r\nforeach ($xml->logentry as $logentry)\r\n{\r\n  $date = date('d\/m\/Y', strtotime((string) $logentry->date));\r\n  foreach ($logentry->paths as $paths)\r\n  {\r\n    foreach ($paths->path as $path)\r\n    {\r\n      if ('A' == $path['action'] && preg_match('#^\/plugins\/.+Plugin$#', (string) $path, $matches))\r\n      {\r\n        if (!isset($numbers[$date]))\r\n        {\r\n          $numbers[$date] = 1;\r\n        }\r\n        else\r\n        {\r\n          ++$numbers[$date];\r\n        }\r\n      }\r\n    }\r\n  }\r\n}<\/pre>\n<p>The $numbers array contains the days as keys, and the number of plugins created for this specific particular day as values.<\/p>\n<p>To create a graph, the array can be exported as a simple CSV file to be read by Excel:<\/p>\n<pre lang=\"php\">foreach ($numbers as $date => $number)\r\n{\r\n  echo \"$date,$number\\r\\n\";\r\n}<\/pre>\n<p>It outputs the following data:<\/p>\n<pre lang=\"text\">02\/02\/2009,1\r\n01\/02\/2009,1\r\n31\/01\/2009,1\r\n30\/01\/2009,2\r\n29\/01\/2009,1\r\n27\/01\/2009,3\r\n...\r\n <\/pre>\n<p>And here is the graph for the data as of today:<\/p>\n<p><a href=\"http:\/\/www.symfony-project.org\/uploads\/assets\/plugin_creation_rate.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.symfony-project.org\/uploads\/assets\/plugin_creation_rate.png\" width=\"750\" height=\"464\" class=\"aligncenter\" \/><\/a><\/p>\n<p>I was right. We more than doubled the number of plugin creation per month after the change: from 14 plugins to 30 plugins per month.<\/p>\n<p><a href=\"http:\/\/fabien.potencier.org\/article\/7\/getting-information-from-svn-with-php\">http:\/\/fabien.potencier.org\/article\/7\/getting-information-from-svn-with-php<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fabien Potencier February 03, 2009 Last year, I deployed a new tool to manage symfony plugins. The first goal of this tool was to simplify the process of contributing new plugins. It proved to be very successful, and I had &hellip; <a href=\"https:\/\/notes.norfolks.fun\/?p=1627\">\u0427\u0438\u0442\u0430\u0442\u044c \u0434\u0430\u043b\u0435\u0435 <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,38],"tags":[],"class_list":["post-1627","post","type-post","status-publish","format-standard","hentry","category-php","category-svn"],"_links":{"self":[{"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=\/wp\/v2\/posts\/1627","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1627"}],"version-history":[{"count":1,"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=\/wp\/v2\/posts\/1627\/revisions"}],"predecessor-version":[{"id":1628,"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=\/wp\/v2\/posts\/1627\/revisions\/1628"}],"wp:attachment":[{"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/notes.norfolks.fun\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}