Source for file PhpSecInfo.php

Documentation is available at PhpSecInfo.php

  1. <?php
  2. /**
  3. * Main class file
  4. *
  5. * @package PhpSecInfo
  6. * @author Ed Finkler <coj@funkatron.com>
  7. */
  8.  
  9.  
  10. /**
  11. * The default language setting if none is set/retrievable
  12. *
  13. */
  14. define ('PHPSECINFO_LANG_DEFAULT', 'en');
  15.  
  16. /**
  17. * a general version string to differentiate releases
  18. *
  19. */
  20. define ('PHPSECINFO_VERSION', '0.2.0');
  21.  
  22. /**
  23. * a YYYYMMDD date string to indicate "build" date
  24. *
  25. */
  26. define ('PHPSECINFO_BUILD', '20070123');
  27.  
  28. /**
  29. * Homepage for phpsecinfo project
  30. *
  31. */
  32. define ('PHPSECINFO_URL', 'http://phpsecinfo.com');
  33.  
  34. /**
  35. * This is the main class for the phpsecinfo system. It's responsible for
  36. * dynamically loading tests, running those tests, and generating the results
  37. * output
  38. *
  39. * Example:
  40. * <code>
  41. * <?php require_once('PhpSecInfo/PhpSecInfo.php'); ?>
  42. * <?php phpsecinfo(); ?>
  43. * </code>
  44. *
  45. * If you want to capture the output, or just grab the test results and display them
  46. * in your own way, you'll need to do slightly more work.
  47. *
  48. * Example:
  49. * <code>
  50. * require_once('PhpSecInfo/PhpSecInfo.php');
  51. * // instantiate the class
  52. * $psi = new PhpSecInfo();
  53. *
  54. * // load and run all tests
  55. * $psi->loadAndRun();
  56. *
  57. * // grab the results as a multidimensional array
  58. * $results = $psi->getResultsAsArray();
  59. * echo "<pre>"; echo print_r($results, true); echo "</pre>";
  60. *
  61. * // grab the standard results output as a string
  62. * $html = $psi->getOutput();
  63. *
  64. * // send it to the browser
  65. * echo $html;
  66. * </code>
  67. *
  68. *
  69. * The procedural function "phpsecinfo" is defined below this class.
  70. * @see phpsecinfo()
  71. *
  72. * @author Ed Finkler <coj@funkatron.com>
  73. *
  74. * see CHANGELOG for changes
  75. *
  76. */
  77. class PhpSecInfo
  78. {
  79.  
  80. /**
  81. * An array of tests to run
  82. *
  83. * @var array PhpSecInfo_Test
  84. */
  85. var $tests_to_run = array();
  86.  
  87. /**
  88. * An array of results. Each result is an associative array:
  89. * <code>
  90. * $result['result'] = PHPSECINFO_TEST_RESULT_NOTICE;
  91. * $result['message'] = "a string describing the test results and what they mean";
  92. * </code>
  93. *
  94. * @var array
  95. */
  96. var $test_results = array();
  97.  
  98.  
  99. /**
  100. * An array of tests that were not run
  101. *
  102. * <code>
  103. * $result['result'] = PHPSECINFO_TEST_RESULT_NOTRUN;
  104. * $result['message'] = "a string explaining why the test was not run";
  105. * </code>
  106. *
  107. * @var array
  108. */
  109. var $tests_not_run = array();
  110.  
  111.  
  112. /**
  113. * The language code used. Defaults to PHPSECINFO_LANG_DEFAULT, which
  114. * is 'en'
  115. *
  116. * @var string
  117. * @see PHPSECINFO_LANG_DEFAULT
  118. */
  119. var $language = PHPSECINFO_LANG_DEFAULT;
  120.  
  121.  
  122. /**
  123. * An array of integers recording the number of test results in each category. Categories can include
  124. * some or all of the PHPSECINFO_TEST_* constants. Constants are the keys, # of results are the values.
  125. *
  126. * @var array
  127. */
  128. var $result_counts = array();
  129.  
  130.  
  131. /**
  132. * The number of tests that have been run
  133. *
  134. * @var integer
  135. */
  136. var $num_tests_run = 0;
  137.  
  138.  
  139. /**
  140. * Constructor
  141. *
  142. * @return PhpSecInfo
  143. */
  144. function PhpSecInfo() {
  145.  
  146. }
  147.  
  148.  
  149. /**
  150. * recurses through the Test subdir and includes classes in each test group subdir,
  151. * then builds an array of classnames for the tests that will be run
  152. *
  153. */
  154. function loadTests() {
  155.  
  156. $test_root = dir(dirname(__FILE__).DIRECTORY_SEPARATOR.'Test');
  157.  
  158. //echo "<pre>"; echo print_r($test_root, true); echo "</pre>";
  159.  
  160. while (false !== ($entry = $test_root->read())) {
  161. if ( is_dir($test_root->path.DIRECTORY_SEPARATOR.$entry) && !preg_match('|^\.(.*)$|', $entry) ) {
  162. $test_dirs[] = $entry;
  163. }
  164. }
  165. //echo "<pre>"; echo print_r($test_dirs, true); echo "</pre>";
  166.  
  167. // include_once all files in each test dir
  168. foreach ($test_dirs as $test_dir) {
  169. $this_dir = dir($test_root->path.DIRECTORY_SEPARATOR.$test_dir);
  170.  
  171. while (false !== ($entry = $this_dir->read())) {
  172. if (!is_dir($this_dir->path.DIRECTORY_SEPARATOR.$entry)) {
  173. include_once $this_dir->path.DIRECTORY_SEPARATOR.$entry;
  174. $classNames[] = "PhpSecInfo_Test_".$test_dir."_".basename($entry, '.php');
  175. }
  176. }
  177.  
  178. }
  179.  
  180. // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
  181. $this->tests_to_run = $classNames;
  182. }
  183.  
  184.  
  185. /**
  186. * This runs the tests in the tests_to_run array and
  187. * places returned data in the following arrays/scalars:
  188. * - $this->test_results
  189. * - $this->result_counts
  190. * - $this->num_tests_run
  191. * - $this->tests_not_run;
  192. *
  193. */
  194. function runTests() {
  195. // initialize a bunch of arrays
  196. $this->test_results = array();
  197. $this->result_counts = array();
  198. $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN] = 0;
  199. $this->num_tests_run = 0;
  200.  
  201. foreach ($this->tests_to_run as $testClass) {
  202.  
  203. /**
  204. * @var $test PhpSecInfo_Test
  205. */
  206. $test = new $testClass();
  207.  
  208. if ($test->isTestable()) {
  209. $test->test();
  210. $rs = array( 'result' => $test->getResult(),
  211. 'message' => $test->getMessage(),
  212. 'value_current' => $test->getCurrentTestValue(),
  213. 'value_recommended' => $test->getRecommendedTestValue(),
  214. 'moreinfo_url' => $test->getMoreInfoURL(),
  215. );
  216. $this->test_results[$test->getTestGroup()][$test->getTestName()] = $rs;
  217.  
  218. // initialize if not yet set
  219. if (!isset ($this->result_counts[$rs['result']]) ) {
  220. $this->result_counts[$rs['result']] = 0;
  221. }
  222.  
  223. $this->result_counts[$rs['result']]++;
  224. $this->num_tests_run++;
  225. } else {
  226. $rs = array( 'result' => $test->getResult(),
  227. 'message' => $test->getMessage(),
  228. 'value_current' => NULL,
  229. 'value_recommended' => NULL,
  230. 'moreinfo_url' => $test->getMoreInfoURL(),
  231. );
  232. $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN]++;
  233. $this->tests_not_run[$test->getTestGroup()."::".$test->getTestName()] = $rs;
  234. }
  235. }
  236. }
  237.  
  238.  
  239. /**
  240. * This is the main output method. The look and feel mimics phpinfo()
  241. *
  242. */
  243. function renderOutput($page_title="Security Information About PHP") {
  244.  
  245. /**
  246. * We need to use PhpSecInfo_Test::getBooleanIniValue() below
  247. * @see PhpSecInfo_Test::getBooleanIniValue()
  248. */
  249. if (!class_exists('PhpSecInfo_Test')) {
  250. include( dirname(__FILE__).DIRECTORY_SEPARATOR.'Test'.DIRECTORY_SEPARATOR.'Test.php');
  251. }
  252.  
  253. ?>
  254. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
  255. <html>
  256. <head>
  257. <title><?php echo $page_title ?></title>
  258. <meta name="robots" content="noindex,nofollow" />
  259. <style type="text/css">
  260. .phpblue { #777BB4 }
  261. /*
  262. #706464
  263. #C7C6B3
  264. #7B8489
  265. #646B70
  266. */
  267.  
  268. BODY {
  269. background-color:#C7C6B3;
  270. color: #333333;
  271. margin: 0;
  272. padding: 0;
  273. text-align:center;
  274. }
  275.  
  276. BODY, TD, TH, H1, H2 {
  277. font-family: Helvetica, Arial, Sans-serif;
  278. }
  279.  
  280. DIV.logo {
  281. float:right;
  282. }
  283.  
  284. A:link, A:hover, A:visited {
  285. color: #000099;
  286. text-decoration: none;
  287. }
  288.  
  289. A:hover {
  290. text-decoration: underline !important;
  291. }
  292.  
  293. DIV.container {
  294. text-align:center;
  295. width:650px;
  296. margin-left:auto;
  297. margin-right:auto;
  298. }
  299.  
  300. DIV.header {
  301. width:100%;
  302. text-align: left;
  303. border-collapse: collapse;
  304. }
  305.  
  306. DIV.header {
  307. background-color:#4C5B74;
  308. color:white;
  309. border-bottom: 3px solid #333333;
  310. padding:.5em;
  311. }
  312.  
  313. DIV.header H1, DIV.header H2 {
  314. padding:0;
  315. margin: 0;
  316. }
  317.  
  318. DIV.header H2 {
  319. font-size: 0.9em;
  320. }
  321.  
  322. DIV.header a:link, DIV.header a:visited, DIV.header a:hover {
  323. color:#ffff99;
  324. }
  325.  
  326. H2.result-header {
  327. margin:1em 0 .5em 0;
  328. }
  329.  
  330. TABLE.results {
  331. border-collapse:collapse;
  332. width:100%;
  333. text-align: left;
  334. }
  335.  
  336. TD, TH {
  337. padding:0.5em;
  338. border: 2px solid #333333;
  339. }
  340.  
  341. TR.header {
  342. background-color:#706464;
  343. color:white;
  344. }
  345.  
  346. TD.label {
  347. text-align:top;
  348. font-weight:bold;
  349. background-color:#7B8489;
  350. border:2px solid #333333;
  351. }
  352.  
  353. TD.value {
  354. border:2px solid #333333 }
  355.  
  356. .centered {
  357. text-align: center;
  358. }
  359. .centered TABLE {
  360. text-align: left;
  361. }
  362. .centered TH { text-align: center; }
  363.  
  364. .result { font-size:1.2em; font-weight:bold; margin-bottom:.5em;}
  365.  
  366. .message { line-height:1.4em; }
  367.  
  368. TABLE.values {
  369. padding:.5em;
  370. margin:.5em;
  371. text-align:left;
  372. margin:none;
  373. width:90%;
  374. }
  375. TABLE.values TD {
  376. font-size:.9em;
  377. border:none;
  378. padding:.4em;
  379. }
  380. TABLE.values TD.label {
  381. font-weight:bold;
  382. text-align:right;
  383. width:40%;
  384. }
  385.  
  386. DIV.moreinfo {
  387. text-align:right;
  388. }
  389.  
  390. .value-ok {background-color:#009900;color:#ffffff;}
  391. .value-ok a:link, .value-ok a:hover, .value-ok a:visited {color:#FFFF99;font-weight:bold;background-color:transparent;text-decoration:none;}
  392. .value-ok table td {background-color:#33AA33; color:#ffffff;}
  393.  
  394. .value-notice {background-color:#FFA500;color:#000000;}
  395. .value-notice a:link, .value-notice a:hover, .value-notice a:visited {color:#000099;font-weight:bold;background-color:transparent;text-decoration:none;}
  396. .value-notice td {background-color:#FFC933; color:#000000;}
  397.  
  398. .value-warn {background-color:#990000;color:#ffffff;}
  399. .value-warn a:link, .value-warn a:hover, .value-warn a:visited {color:#FFFF99;font-weight:bold;background-color:transparent;text-decoration:none;}
  400. .value-warn td {background-color:#AA3333; color:#ffffff;}
  401.  
  402. .value-notrun {background-color:#cccccc;color:#000000;}
  403. .value-notrun a:link, .value-notrun a:hover, .value-notrun a:visited {color:#000099;font-weight:bold;background-color:transparent;text-decoration:none;}
  404. .value-notrun td {background-color:#dddddd; color:#000000;}
  405.  
  406. .value-error {background-color:#F6AE15;color:#000000;font-weight:bold;}
  407. .value-error td {background-color:#F6AE15; color:#000000;}
  408.  
  409.  
  410.  
  411. </style>
  412.  
  413. </head>
  414. <body>
  415. <div class="header">
  416. <h1><?php echo $page_title ?></h1>
  417. <h2>PhpSecInfo Version <?php echo PHPSECINFO_VERSION ?>; build <?php echo PHPSECINFO_BUILD ?> &middot; <a href="<?php echo PHPSECINFO_URL ?>">Project Homepage</a></h2>
  418. </div>
  419.  
  420. <div class="container">
  421.  
  422.  
  423. <?php
  424. foreach ($this->test_results as $group_name=>$group_results) {
  425. $this->_outputRenderTable($group_name, $group_results);
  426. }
  427.  
  428. $this->_outputRenderNotRunTable();
  429.  
  430. $this->_outputRenderStatsTable();
  431.  
  432. ?>
  433.  
  434. </div>
  435. </body>
  436. </html>
  437. <?php
  438. }
  439.  
  440.  
  441. /**
  442. * This is a helper method that makes it easy to output tables of test results
  443. * for a given test group
  444. *
  445. * @param string $group_name
  446. * @param array $group_results
  447. */
  448. function _outputRenderTable($group_name, $group_results) {
  449.  
  450. // exit out if $group_results was empty or not an array. This sorta seems a little hacky...
  451. if (!is_array($group_results) || sizeof($group_results) < 1) {
  452. return false;
  453. }
  454.  
  455. ksort($group_results);
  456.  
  457. ?>
  458. <h2 class="result-header"><?php echo htmlspecialchars($group_name, ENT_QUOTES) ?></h2>
  459.  
  460. <table class="results">
  461. <tr class="header">
  462. <th>Test</th>
  463. <th>Result</th>
  464. </tr>
  465. <?php foreach ($group_results as $test_name=>$test_results): ?>
  466.  
  467. <tr>
  468. <td class="label"><?php echo htmlspecialchars($test_name, ENT_QUOTES) ?></td>
  469. <td class="value <?php echo $this->_outputGetCssClassFromResult($test_results['result']) ?>">
  470. <?php if ($group_name != 'Test Results Summary'): ?>
  471. <div class="result"><?php echo $this->_outputGetResultTypeFromCode($test_results['result']) ?></div>
  472. <?php endif; ?>
  473. <div class="message"><?php echo $test_results['message'] ?></div>
  474.  
  475. <table class="values">
  476. <?php if (isset($test_results['value_current'])): ?>
  477. <tr>
  478. <td class="label">Current Value:</td>
  479. <td><?php echo $test_results['value_current'] ?></td>
  480. </tr>
  481. <?php endif;?>
  482. <?php if (isset($test_results['value_recommended'])): ?>
  483. <tr>
  484. <td class="label">Recommended Value:</td>
  485. <td><?php echo $test_results['value_recommended'] ?></td>
  486. </tr>
  487. <?php endif; ?>
  488. </table>
  489. <?php if (isset($test_results['moreinfo_url']) && $test_results['moreinfo_url']): ?>
  490. <div class="moreinfo"><a href="<?php echo $test_results['moreinfo_url']; ?>">More information &raquo;</a></div>
  491. <?php endif; ?>
  492. </td>
  493. </tr>
  494.  
  495. <?php endforeach; ?>
  496. </table><br />
  497.  
  498. <?php
  499. return true;
  500. }
  501.  
  502.  
  503.  
  504. /**
  505. * This outputs a table containing a summary of the test results (counts and % in each result type)
  506. *
  507. * @see PHPSecInfo::_outputRenderTable()
  508. * @see PHPSecInfo::_outputGetResultTypeFromCode()
  509. */
  510. function _outputRenderStatsTable() {
  511.  
  512. foreach($this->result_counts as $code=>$val) {
  513. if ($code != PHPSECINFO_TEST_RESULT_NOTRUN) {
  514. $percentage = round($val/$this->num_tests_run * 100,2);
  515.  
  516. $stats[$this->_outputGetResultTypeFromCode($code)] = array( 'count' => $val,
  517. 'result' => $code,
  518. 'message' => "$val out of {$this->num_tests_run} ($percentage%)");
  519. }
  520. }
  521.  
  522. $this->_outputRenderTable('Test Results Summary', $stats);
  523.  
  524. }
  525.  
  526.  
  527.  
  528. /**
  529. * This outputs a table containing a summary or test that were not executed, and the reasons why they were skipped
  530. *
  531. * @see PHPSecInfo::_outputRenderTable()
  532. */
  533. function _outputRenderNotRunTable() {
  534.  
  535. $this->_outputRenderTable('Tests Not Run', $this->tests_not_run);
  536.  
  537. }
  538.  
  539.  
  540.  
  541.  
  542. /**
  543. * This is a helper function that returns a CSS class corresponding to
  544. * the result code the test returned. This allows us to color-code
  545. * results
  546. *
  547. * @param integer $code
  548. * @return string
  549. */
  550. function _outputGetCssClassFromResult($code) {
  551.  
  552. switch ($code) {
  553. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_OK">PHPSECINFO_TEST_RESULT_OK</a>:
  554. return 'value-ok';
  555. break;
  556.  
  557. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTICE">PHPSECINFO_TEST_RESULT_NOTICE</a>:
  558. return 'value-notice';
  559. break;
  560.  
  561. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_WARN">PHPSECINFO_TEST_RESULT_WARN</a>:
  562. return 'value-warn';
  563. break;
  564.  
  565. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTRUN">PHPSECINFO_TEST_RESULT_NOTRUN</a>:
  566. return 'value-notrun';
  567. break;
  568.  
  569. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_ERROR">PHPSECINFO_TEST_RESULT_ERROR</a>:
  570. return 'value-error';
  571. break;
  572.  
  573. default:
  574. return 'value-notrun';
  575. break;
  576. }
  577.  
  578. }
  579.  
  580.  
  581.  
  582. /**
  583. * This is a helper function that returns a label string corresponding to
  584. * the result code the test returned. This is mainly used for the Test
  585. * Results Summary table.
  586. *
  587. * @see PHPSecInfo::_outputRenderStatsTable()
  588. * @param integer $code
  589. * @return string
  590. */
  591. function _outputGetResultTypeFromCode($code) {
  592.  
  593. switch ($code) {
  594. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_OK">PHPSECINFO_TEST_RESULT_OK</a>:
  595. return 'Pass';
  596. break;
  597.  
  598. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTICE">PHPSECINFO_TEST_RESULT_NOTICE</a>:
  599. return 'Notice';
  600. break;
  601.  
  602. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_WARN">PHPSECINFO_TEST_RESULT_WARN</a>:
  603. return 'Warning';
  604. break;
  605.  
  606. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTRUN">PHPSECINFO_TEST_RESULT_NOTRUN</a>:
  607. return 'Not Run';
  608. break;
  609.  
  610. case <a href="../PhpSecInfo/_trunk_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_ERROR">PHPSECINFO_TEST_RESULT_ERROR</a>:
  611. return 'Error';
  612. break;
  613.  
  614. default:
  615. return 'Invalid Result Code';
  616. break;
  617. }
  618.  
  619. }
  620.  
  621.  
  622. /**
  623. * Loads and runs all the tests
  624. *
  625. * As loading, then running, is a pretty common process, this saves a extra method call
  626. *
  627. * @since 0.1.1
  628. *
  629. */
  630. function loadAndRun() {
  631. $this->loadTests();
  632. $this->runTests();
  633. }
  634.  
  635.  
  636. /**
  637. * returns an associative array of test data. Four keys are set:
  638. * - test_results (array)
  639. * - tests_not_run (array)
  640. * - result_counts (array)
  641. * - num_tests_run (integer)
  642. *
  643. * note that this must be called after tests are loaded and run
  644. *
  645. * @since 0.1.1
  646. * @return array
  647. */
  648. function getResultsAsArray() {
  649. $results = array();
  650.  
  651. $results['test_results'] = $this->test_results;
  652. $results['tests_not_run'] = $this->tests_not_run;
  653. $results['result_counts'] = $this->result_counts;
  654. $results['num_tests_run'] = $this->num_tests_run;
  655.  
  656. return $results;
  657. }
  658.  
  659.  
  660.  
  661. /**
  662. * returns the standard output as a string instead of echoing it to the browser
  663. *
  664. * note that this must be called after tests are loaded and run
  665. *
  666. * @since 0.1.1
  667. *
  668. * @return string
  669. */
  670. function getOutput() {
  671. ob_start();
  672. $this->renderOutput();
  673. $output = ob_get_clean();
  674. return $output;
  675. }
  676.  
  677.  
  678.  
  679.  
  680. }
  681.  
  682.  
  683.  
  684.  
  685. /**
  686. * A globally-available function that runs the tests and creates the result page
  687. *
  688. */
  689. function phpsecinfo() {
  690. // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
  691. $psi = new PhpSecInfo();
  692. $psi->loadAndRun();
  693. $psi->renderOutput();

Documentation generated on Mon, 26 Feb 2007 10:50:02 -0500 by phpDocumentor 1.3.0RC3