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.1');
  21.  
  22. /**
  23. * a YYYYMMDD date string to indicate "build" date
  24. *
  25. */
  26. define ('PHPSECINFO_BUILD', '20070329');
  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 http-equiv="Content-Type" content="text/html;charset=utf-8" />
  259. <meta name="robots" content="noindex,nofollow" />
  260. <style type="text/css">
  261. .phpblue { #777BB4 }
  262. /*
  263. #706464
  264. #C7C6B3
  265. #7B8489
  266. #646B70
  267. */
  268.  
  269. BODY {
  270. background-color:#C7C6B3;
  271. color: #333333;
  272. margin: 0;
  273. padding: 0;
  274. text-align:center;
  275. }
  276.  
  277. BODY, TD, TH, H1, H2 {
  278. font-family: Helvetica, Arial, Sans-serif;
  279. }
  280.  
  281. DIV.logo {
  282. float:right;
  283. }
  284.  
  285. A:link, A:hover, A:visited {
  286. color: #000099;
  287. text-decoration: none;
  288. }
  289.  
  290. A:hover {
  291. text-decoration: underline !important;
  292. }
  293.  
  294. DIV.container {
  295. text-align:center;
  296. width:650px;
  297. margin-left:auto;
  298. margin-right:auto;
  299. }
  300.  
  301. DIV.header {
  302. width:100%;
  303. text-align: left;
  304. border-collapse: collapse;
  305. }
  306.  
  307. DIV.header {
  308. background-color:#4C5B74;
  309. color:white;
  310. border-bottom: 3px solid #333333;
  311. padding:.5em;
  312. }
  313.  
  314. DIV.header H1, DIV.header H2 {
  315. padding:0;
  316. margin: 0;
  317. }
  318.  
  319. DIV.header H2 {
  320. font-size: 0.9em;
  321. }
  322.  
  323. DIV.header a:link, DIV.header a:visited, DIV.header a:hover {
  324. color:#ffff99;
  325. }
  326.  
  327. H2.result-header {
  328. margin:1em 0 .5em 0;
  329. }
  330.  
  331. TABLE.results {
  332. border-collapse:collapse;
  333. width:100%;
  334. text-align: left;
  335. }
  336.  
  337. TD, TH {
  338. padding:0.5em;
  339. border: 2px solid #333333;
  340. }
  341.  
  342. TR.header {
  343. background-color:#706464;
  344. color:white;
  345. }
  346.  
  347. TD.label {
  348. text-align:top;
  349. font-weight:bold;
  350. background-color:#7B8489;
  351. border:2px solid #333333;
  352. }
  353.  
  354. TD.value {
  355. border:2px solid #333333 }
  356.  
  357. .centered {
  358. text-align: center;
  359. }
  360. .centered TABLE {
  361. text-align: left;
  362. }
  363. .centered TH { text-align: center; }
  364.  
  365. .result { font-size:1.2em; font-weight:bold; margin-bottom:.5em;}
  366.  
  367. .message { line-height:1.4em; }
  368.  
  369. TABLE.values {
  370. padding:.5em;
  371. margin:.5em;
  372. text-align:left;
  373. margin:none;
  374. width:90%;
  375. }
  376. TABLE.values TD {
  377. font-size:.9em;
  378. border:none;
  379. padding:.4em;
  380. }
  381. TABLE.values TD.label {
  382. font-weight:bold;
  383. text-align:right;
  384. width:40%;
  385. }
  386.  
  387. DIV.moreinfo {
  388. text-align:right;
  389. }
  390.  
  391. .value-ok {background-color:#009900;color:#ffffff;}
  392. .value-ok a:link, .value-ok a:hover, .value-ok a:visited {color:#FFFF99;font-weight:bold;background-color:transparent;text-decoration:none;}
  393. .value-ok table td {background-color:#33AA33; color:#ffffff;}
  394.  
  395. .value-notice {background-color:#FFA500;color:#000000;}
  396. .value-notice a:link, .value-notice a:hover, .value-notice a:visited {color:#000099;font-weight:bold;background-color:transparent;text-decoration:none;}
  397. .value-notice td {background-color:#FFC933; color:#000000;}
  398.  
  399. .value-warn {background-color:#990000;color:#ffffff;}
  400. .value-warn a:link, .value-warn a:hover, .value-warn a:visited {color:#FFFF99;font-weight:bold;background-color:transparent;text-decoration:none;}
  401. .value-warn td {background-color:#AA3333; color:#ffffff;}
  402.  
  403. .value-notrun {background-color:#cccccc;color:#000000;}
  404. .value-notrun a:link, .value-notrun a:hover, .value-notrun a:visited {color:#000099;font-weight:bold;background-color:transparent;text-decoration:none;}
  405. .value-notrun td {background-color:#dddddd; color:#000000;}
  406.  
  407. .value-error {background-color:#F6AE15;color:#000000;font-weight:bold;}
  408. .value-error td {background-color:#F6AE15; color:#000000;}
  409.  
  410.  
  411.  
  412. </style>
  413.  
  414. </head>
  415. <body>
  416. <div class="header">
  417. <h1><?php echo $page_title ?></h1>
  418. <h2>PhpSecInfo Version <?php echo PHPSECINFO_VERSION ?>; build <?php echo PHPSECINFO_BUILD ?> &middot; <a href="<?php echo PHPSECINFO_URL ?>">Project Homepage</a></h2>
  419. </div>
  420.  
  421. <div class="container">
  422.  
  423.  
  424. <?php
  425. foreach ($this->test_results as $group_name=>$group_results) {
  426. $this->_outputRenderTable($group_name, $group_results);
  427. }
  428.  
  429. $this->_outputRenderNotRunTable();
  430.  
  431. $this->_outputRenderStatsTable();
  432.  
  433. ?>
  434.  
  435. </div>
  436. </body>
  437. </html>
  438. <?php
  439. }
  440.  
  441.  
  442. /**
  443. * This is a helper method that makes it easy to output tables of test results
  444. * for a given test group
  445. *
  446. * @param string $group_name
  447. * @param array $group_results
  448. */
  449. function _outputRenderTable($group_name, $group_results) {
  450.  
  451. // exit out if $group_results was empty or not an array. This sorta seems a little hacky...
  452. if (!is_array($group_results) || sizeof($group_results) < 1) {
  453. return false;
  454. }
  455.  
  456. ksort($group_results);
  457.  
  458. ?>
  459. <h2 class="result-header"><?php echo htmlspecialchars($group_name, ENT_QUOTES) ?></h2>
  460.  
  461. <table class="results">
  462. <tr class="header">
  463. <th>Test</th>
  464. <th>Result</th>
  465. </tr>
  466. <?php foreach ($group_results as $test_name=>$test_results): ?>
  467.  
  468. <tr>
  469. <td class="label"><?php echo htmlspecialchars($test_name, ENT_QUOTES) ?></td>
  470. <td class="value <?php echo $this->_outputGetCssClassFromResult($test_results['result']) ?>">
  471. <?php if ($group_name != 'Test Results Summary'): ?>
  472. <div class="result"><?php echo $this->_outputGetResultTypeFromCode($test_results['result']) ?></div>
  473. <?php endif; ?>
  474. <div class="message"><?php echo $test_results['message'] ?></div>
  475.  
  476. <?php if ( isset($test_results['value_current'] ) || isset($test_results['value_recommended']) ): ?>
  477. <table class="values">
  478. <?php if (isset($test_results['value_current'])): ?>
  479. <tr>
  480. <td class="label">Current Value:</td>
  481. <td><?php echo $test_results['value_current'] ?></td>
  482. </tr>
  483. <?php endif;?>
  484. <?php if (isset($test_results['value_recommended'])): ?>
  485. <tr>
  486. <td class="label">Recommended Value:</td>
  487. <td><?php echo $test_results['value_recommended'] ?></td>
  488. </tr>
  489. <?php endif; ?>
  490. </table>
  491. <?php endif; ?>
  492.  
  493. <?php if (isset($test_results['moreinfo_url']) && $test_results['moreinfo_url']): ?>
  494. <div class="moreinfo"><a href="<?php echo $test_results['moreinfo_url']; ?>">More information &raquo;</a></div>
  495. <?php endif; ?>
  496. </td>
  497. </tr>
  498.  
  499. <?php endforeach; ?>
  500. </table><br />
  501.  
  502. <?php
  503. return true;
  504. }
  505.  
  506.  
  507.  
  508. /**
  509. * This outputs a table containing a summary of the test results (counts and % in each result type)
  510. *
  511. * @see PHPSecInfo::_outputRenderTable()
  512. * @see PHPSecInfo::_outputGetResultTypeFromCode()
  513. */
  514. function _outputRenderStatsTable() {
  515.  
  516. foreach($this->result_counts as $code=>$val) {
  517. if ($code != PHPSECINFO_TEST_RESULT_NOTRUN) {
  518. $percentage = round($val/$this->num_tests_run * 100,2);
  519.  
  520. $stats[$this->_outputGetResultTypeFromCode($code)] = array( 'count' => $val,
  521. 'result' => $code,
  522. 'message' => "$val out of {$this->num_tests_run} ($percentage%)");
  523. }
  524. }
  525.  
  526. $this->_outputRenderTable('Test Results Summary', $stats);
  527.  
  528. }
  529.  
  530.  
  531.  
  532. /**
  533. * This outputs a table containing a summary or test that were not executed, and the reasons why they were skipped
  534. *
  535. * @see PHPSecInfo::_outputRenderTable()
  536. */
  537. function _outputRenderNotRunTable() {
  538.  
  539. $this->_outputRenderTable('Tests Not Run', $this->tests_not_run);
  540.  
  541. }
  542.  
  543.  
  544.  
  545.  
  546. /**
  547. * This is a helper function that returns a CSS class corresponding to
  548. * the result code the test returned. This allows us to color-code
  549. * results
  550. *
  551. * @param integer $code
  552. * @return string
  553. */
  554. function _outputGetCssClassFromResult($code) {
  555.  
  556. switch ($code) {
  557. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_OK">PHPSECINFO_TEST_RESULT_OK</a>:
  558. return 'value-ok';
  559. break;
  560.  
  561. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTICE">PHPSECINFO_TEST_RESULT_NOTICE</a>:
  562. return 'value-notice';
  563. break;
  564.  
  565. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_WARN">PHPSECINFO_TEST_RESULT_WARN</a>:
  566. return 'value-warn';
  567. break;
  568.  
  569. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTRUN">PHPSECINFO_TEST_RESULT_NOTRUN</a>:
  570. return 'value-notrun';
  571. break;
  572.  
  573. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_ERROR">PHPSECINFO_TEST_RESULT_ERROR</a>:
  574. return 'value-error';
  575. break;
  576.  
  577. default:
  578. return 'value-notrun';
  579. break;
  580. }
  581.  
  582. }
  583.  
  584.  
  585.  
  586. /**
  587. * This is a helper function that returns a label string corresponding to
  588. * the result code the test returned. This is mainly used for the Test
  589. * Results Summary table.
  590. *
  591. * @see PHPSecInfo::_outputRenderStatsTable()
  592. * @param integer $code
  593. * @return string
  594. */
  595. function _outputGetResultTypeFromCode($code) {
  596.  
  597. switch ($code) {
  598. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_OK">PHPSECINFO_TEST_RESULT_OK</a>:
  599. return 'Pass';
  600. break;
  601.  
  602. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTICE">PHPSECINFO_TEST_RESULT_NOTICE</a>:
  603. return 'Notice';
  604. break;
  605.  
  606. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_WARN">PHPSECINFO_TEST_RESULT_WARN</a>:
  607. return 'Warning';
  608. break;
  609.  
  610. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_NOTRUN">PHPSECINFO_TEST_RESULT_NOTRUN</a>:
  611. return 'Not Run';
  612. break;
  613.  
  614. case <a href="../PhpSecInfo/_PhpSecInfo_Test_Test_php.html#definePHPSECINFO_TEST_RESULT_ERROR">PHPSECINFO_TEST_RESULT_ERROR</a>:
  615. return 'Error';
  616. break;
  617.  
  618. default:
  619. return 'Invalid Result Code';
  620. break;
  621. }
  622.  
  623. }
  624.  
  625.  
  626. /**
  627. * Loads and runs all the tests
  628. *
  629. * As loading, then running, is a pretty common process, this saves a extra method call
  630. *
  631. * @since 0.1.1
  632. *
  633. */
  634. function loadAndRun() {
  635. $this->loadTests();
  636. $this->runTests();
  637. }
  638.  
  639.  
  640. /**
  641. * returns an associative array of test data. Four keys are set:
  642. * - test_results (array)
  643. * - tests_not_run (array)
  644. * - result_counts (array)
  645. * - num_tests_run (integer)
  646. *
  647. * note that this must be called after tests are loaded and run
  648. *
  649. * @since 0.1.1
  650. * @return array
  651. */
  652. function getResultsAsArray() {
  653. $results = array();
  654.  
  655. $results['test_results'] = $this->test_results;
  656. $results['tests_not_run'] = $this->tests_not_run;
  657. $results['result_counts'] = $this->result_counts;
  658. $results['num_tests_run'] = $this->num_tests_run;
  659.  
  660. return $results;
  661. }
  662.  
  663.  
  664.  
  665. /**
  666. * returns the standard output as a string instead of echoing it to the browser
  667. *
  668. * note that this must be called after tests are loaded and run
  669. *
  670. * @since 0.1.1
  671. *
  672. * @return string
  673. */
  674. function getOutput() {
  675. ob_start();
  676. $this->renderOutput();
  677. $output = ob_get_clean();
  678. return $output;
  679. }
  680.  
  681.  
  682.  
  683.  
  684. }
  685.  
  686.  
  687.  
  688.  
  689. /**
  690. * A globally-available function that runs the tests and creates the result page
  691. *
  692. */
  693. function phpsecinfo() {
  694. // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
  695. $psi = new PhpSecInfo();
  696. $psi->loadAndRun();
  697. $psi->renderOutput();

Documentation generated on Fri, 06 Apr 2007 13:32:17 -0400 by phpDocumentor 1.3.0RC3