| Erfahrener Benutzer
Registriert seit: 21.05.2008
Beiträge: 9.937
| Die nächste Erweiterung sind die Debug::argument() und Debug::arguments() Methoden, mit denen sich Argumente/Parameter einer fremden Funktion oder Methode testen lassen. Man entschuldige die wieder mal gewechselte Variablenschreibweise meinerseits. PHP-Code: <?php
function myFunc($integer, $string, $objectStdClass, $array)
{
System::arguments(func_get_args(), array('integer', 'string', 'object(stdclass)', 'array(key1,key2)'));
// ..
}
?> Werden der Funktion myFunc nun (in dieser Reihenfolge) nicht ein Integer, String, ein stdClass-Objekt oder Array mit den Schlüsseln key1 und key2 übergeben, löst ein trigger_error() einen Notice aus. Natürlich nur wenn Debugging aktiviert ist.
Mögliche Einstellung sind alle gettype() Rückgabewerte, bei Arrays zusätzlich Code: array(N); // N = Anzahl der Elemente
array(N,i); // i steht für die Eigenschaft, dass das Array bei 0 beginnende fortlaufende Indexe hat (also für for() geeignet ist)
array(X,Y,Z); // X, Y und Z stehten für Schlüssel, die in diesem Array vorhanden sein müssen, alphabetische Reihenfolge vorausgesetzt
Objekte mittels Code: object(C); // C = Klassen-Name
Hinzukommen wird demnächst noch die Prüfung auf einen Array deren Elemente Objekte einer Klasse sind, in etwa Code: array(object(stdclass))
Ich weiß dass PHP 5 bereits eine implementierte Syntax für Objekte als Parameter anbietet, nichtsdestotrotz benötigte ich das ganze für alle Variablen-Typen und auch für PHP 4.
Ich brauche recht häufig den Check auf Argumente, zugegebenermaßen kann man flexibler sein, wenn man sich das ganze fix ausschreibt, allerdings hat sich dabei doch sehr viel Code wiederholt. Das wollte ich hierdurch vermeiden.
Da es weiterhin den Debug::off() Aufruf gibt bzw. man das Error-Level für Produktiv-Umgebungen ja sowieso hochstellt, finde ich das ganze sehr angenehm. PHP-Code: <?php
/**
* @author Christian Reinecke <christian.reinecke@web.de>
* @date 2007-05-16
*
* little helper for debug output
* the methods are all static, there is no static declaration just because of downward compatibility
* use only the public static methods
* all methods starting with an underscore like _display() are for internal static use only - do not use them
*/
/**
* defines the name of the global variable used to save the state of the debug mode (on|off)
* if you have any conflicts in your namespace with this name, change it here
*/
define('DEBUG_GLOBAL_VAR', '__DEBUG_ON');
${DEBUG_GLOBAL_VAR} = true;
/**
* defines the error message and warning level for a bad argument
*/
define('DEBUG_ERROR_ARGUMENT', '%s(): Argument#%u (%s) should be of type %s in [b]%s[/b] on line [b]%u[/b], triggered actually');
define('DEBUG_ERRNO_ARGUMENT', E_USER_NOTICE);
class Debug
{
/**
* @example
* Debug::on(); // optional
* Debug::write(new stdClass, array(1,2), 5, 6.0, 'seven', null, false);
* Debug::write("hallo", "hello", "salut");
* Debug::off();
* Debug::write("you'll never see that output");
* Debug::on();
* Debug::table(array('one' => 'two', 'three' => 'four', 'five' => array('six')));
* Debug::report('SEEG'); // puts out the Arays SE(ssion), E(nv) and G(et) in this order
*
* thanks to Dominik Bonsch <info@web-modules.de> for some code snippets and inspiration (report/table)
*
* you can also check arguments
* @example
* Debug::on(); // optional
* function MyFunction($integer) {
* System::argument($integer, 'integer');
* // ..
* }
* // in case that $integer is not an integer a warning is triggered ({DEBUG_ERRNO_ARGUMENT})
* class MyClass {
* function myMethod($object, $array1, $array2, $string) {
* System::arguments(func_get_args(), array('object(stdClass)', 'array(key1,key2)', 'array(2)', 'string'));
* // ..
* }
* // same procedure
* }
*/
/**
* reactivate debug output (default is on)
* @access public
* @return void
* @example Debug::on()
*/
function on()
{
$GLOBALS[DEBUG_GLOBAL_VAR] = true;
}
/**
* deactivate debug output
* @access public
* @return void
* @example Debug::off()
*/
function off()
{
$GLOBALS[DEBUG_GLOBAL_VAR] = false;
}
/**
* puts some given variables out
* @access public
* @return void
* @example Debug::write($myArray, $myInteger, $mObject, $myString, ..);
*/
function write(/** $var1, $var2, .. **/)
{
if (!$GLOBALS[DEBUG_GLOBAL_VAR]) {
return;
}
$aArgv = func_get_args();
for ($i = 0, $k = 1, $x = count($aArgv); $i < $x; ++$i, ++$k) {
$aOutput[] = Debug::_var($aArgv[$i], "ARG $k");
}
$aOutput[] = Debug::_backtrace();
Debug::_display($aOutput);
}
/**
* puts some given variables out and stops afterwards
* @access public
* @return void
* @example Debug::stop($myArray, $myInteger, $mObject, $myString, ..);
*/
function stop(/** $var1, $var2, .. **/)
{
if (!$GLOBALS[DEBUG_GLOBAL_VAR]) {
return;
}
$aOutput = array();
$aArgv = func_get_args();
for ($i = 0, $k = 1, $x = count($aArgv); $i < $x; ++$i, ++$k) {
$aOutput[] = Debug::_var($aArgv[$i], "ARG $k");
}
$aOutput[] = Debug::_backtrace();
Debug::_display($aOutput, true);
}
/**
* puts the content of the superglobals $_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_ENV and $_FILES out.
* @param string $sSuperGlobals list of char shortcuts to order the output, default available
* @param boolean $bExit exit afterwards
* @return void
* @example Debug::report('GPF', 1);
*/
function report($sSuperGlobals = 'GPCSESVEF', $bExit = false)
{
if (!$GLOBALS[DEBUG_GLOBAL_VAR]) {
return;
}
$aSuperGlobals = array('G' => array('GET', $_GET),
'P' => array('POST', $_POST),
'C' => array('COOKIE', $_COOKIE),
'SE' => array('SESSION', $_SESSION),
'SV' => array('SERVER', $_SERVER),
'E' => array('ENV', $_ENV),
'F' => array('FILES', $_FILES,
'GL' => array('GLOBALS', $GLOBALS)));
$sSuperGlobals = strtoupper($sSuperGlobals);
$aOutput = array();
foreach ($aSuperGlobals as $sChar => $aSuperGlobal) {
if (($iPos = strpos($sSuperGlobals, $sChar)) !== false) {
#var_dump("vorher $sSuperGlobals");
$sSuperGlobals = substr($sSuperGlobals, 0, $iPos) . str_pad('', strlen($sChar), ' ') . substr($sSuperGlobals, $iPos + strlen($sChar));
#var_dump("nachher $sSuperGlobals");
#var_dump($iPos * 2);
$aOutput[$iPos * 2 + 0] = Debug::_msg($aSuperGlobal[0]);
$aOutput[$iPos * 2 + 1] = Debug::table($aSuperGlobal[1], false);
}
}
if (count($aOutput) == 0) {
trigger_error('Debug::report(): Argument #1 should be a string containing at least one of the following char combinations: ' .
implode(', ', array_keys($aSuperGlobals)),
E_USER_NOTICE);
return null;
}
ksort($aOutput);
#$aOutput = array_merge($aOutput);
$aOutput[] = Debug::_backtrace();
Debug::_display($aOutput, $bExit);
}
/**
* puts the content of an array out
* @access public
* @param array $aArray
* @param boolean $bPrint print, othervise return (set automatically on recursion)
* @param string $sPreTitle title attribute for cells (set automatically on recursion)
* @param integer $iDepth recursion depth (used to detect recursion)
* @return mixed depends on $bPrint
* @example Debug::table($_SESSION); this is by the same: Debug::report('SE')
*/
function table($aArray, $bPrint = true, $sPreTitle = '', $iDepth = 0)
{
if (!$GLOBALS[DEBUG_GLOBAL_VAR]) {
return;
}
$aOutput = array();
if ($iDepth == 0) {
$aOutput[] = Debug::_msg('TABLE');
}
$aOutput[] = '<pre class="debug_class"><table border="1" cellspacing="0" class="debug_class">';
if (!is_array($aArray)) {
$aOutput[] = sprintf("<tr><th>[i]%s[/i]</td></tr>", strtoupper(gettype($aArray)));
} else if (count($aArray) == 0) {
$aOutput[] = '<tr><th>[i]EMPTY[/i]</td></tr>';
} else {
foreach ($aArray as $sKey => $mValue) {
$sCurrentTitle = "{$sPreTitle}[{$sKey}]";
if (is_array($mValue)) {
$sContent = Debug::table($mValue, false, $sCurrentTitle, $iDepth + 1);
} else {
ob_start();
var_dump($mValue);
$sContent = ob_get_contents();
ob_end_clean();
}
$aOutput[] = sprintf('<tr title="%s"><th>%s</th><td>%s</td></tr>', $sCurrentTitle, $sKey, $sContent);
}
}
$aOutput[] = '</table></pre>';
if (!$bPrint) {
return implode("\n", $aOutput);
} else {
$aOutput[] = Debug::_backtrace();
Debug::_display($aOutput);
}
}
/**
* checks whether the given argument has the expected type
*
* @static
* @access public
* @param mixed $argument argument to check
* @param string $expected expected type of the argument
* @param integer $argumentNumber argument number
* @param integer $additionalBacksteps increase once for every function/method calling
* {Debug::argument()} not directly for use (f.e.
* {Debug::arguments()})
* @return boolean argument matches the expected type
*/
function argument($arg, $expected, $argumentNumber = 1, $steps = 0)
{
if (!$GLOBALS[DEBUG_GLOBAL_VAR]) {
return;
}
if (is_null($arg)) {
return true;
}
$is = Debug::_analyseArgument($arg);
/**
* this is a case-insensitive check, limiting the compare to the
* length of the expected string value, making it possible to compare for the type only
* although using the more complex return type of {Debug::analyse()}
* @example
* strncasecmp($is = 'object(stdclass)', $expected = 'object') -> TRUE
* strncasecmp($is = 'object(stdclass)', $expected = 'object(stdclass)') -> TRUE (sure)
* strncasecmp($is = 'object(stdclass)', $expected = 'Object(stdClass)') -> TRUE
* strncasecmp($is = 'array(2,i)', $expected = 'array(2)') -> TRUE
* strncasecmp($is = 'array(2)', $expected = 'array(3)') -> FALSE (sure)
* etc.
* */
if (strncasecmp($is, $expected, strlen($expected) - 1) != 0) {
Debug::_triggerArgument($argumentNumber, $is, $expected, $steps);
return false;
}
return true;
}
/**
* checks multiple arguments
*
* @static
* @access public
* @param array $argv array of arguments
* @param array $argvExpected array of the expected types
* @return boolean all arguments passed the check
*/
function arguments($argv, $argvExpected)
{
if (!$GLOBALS[DEBUG_GLOBAL_VAR]) {
return;
}
for ($i = 0, $x = min(count($argv), count($argvExpected)); $i < $x; ++$i) {
if (!Debug::argument($argv[$i], $argvExpected[$i], $i + 1, 1)) {
return false;
}
}
return true;
}
/**
* display
* @access private
*/
function _display($aOutput, $bExit = false)
{
Debug::argument($aOutput, 'array');
if ($bExit) {
$aOutput[] = '';
$aOutput[] = Debug::_msg('STOP');
}
print '<pre class="debug">' . implode("\n", $aOutput) .'</pre>';
if ($bExit) {
exit;
}
}
/**
* @access private
*/
function _msg($sMsg = null)
{
if (isset($sMsg)) {
return 'DEBUG ' . strtoupper($sMsg);
} else {
return '';
}
}
/**
* @access private
*/
function _var($mVariable, $sMsg = '', $bShowType = true)
{
ob_start();
if (!empty($sMsg)) {
print Debug::_msg($sMsg);
print "\n";
}
if ($bShowType) {
var_dump($mVariable);
} else {
// second argument is supported since 4.3, let's fix it with output buffering
print_r($mVariable);
}
$sOutput = ob_get_contents();
ob_end_clean();
return $sOutput;
}
/**
* @access private
* @warning do not use the debug_backtrace*() functions as parameter for any method of this class
* othervise it might end in an infinite loop
*/
function _backtrace()
{
$aBacktrace = debug_backtrace();
$aOutput = array(Debug::_msg('BACKTRACE'));
for ($i = 1, $x = count($aBacktrace); $i < $x; ++$i) {
$aOutput[] = sprintf('[%u] %s:%u', $x - $i, $aBacktrace[$i]['file'], $aBacktrace[$i]['line']);
}
if (count($aOutput) == 0) {
return '';
}
return implode("\n", $aOutput);
}
/**
* triggers in case of an argument error
*
* @static
* @access private
* @param integer $argumentNumber number of the argument
* @param mixed $argument argument that triggered the error
* @param mixed $expected type the argument should acutally be
* @param integer $additionalBacksteps relevant backtrace step
*/
function _triggerArgument($argumentNumber, $is, $expected, $step)
{
// get backtrace route and cut off {System} entries
$backtrace = debug_backtrace();
$backtrace = $backtrace[2 + $step];
$call = ltrim(@$backtrace['class'] . '::' . $backtrace['function'], ':');
trigger_error(sprintf(SYSTEM_ERROR_ARGUMENT, $call, $argumentNumber, $is, $expected,
$backtrace['file'], $backtrace['line']),
SYSTEM_ERRNO_ARGUMENT);
}
/**
* analyses a given argument
* @static
* @access private
* @param mixed $argument argument to analyse
* @return string argument type
*/
function _analyseArgument($argument)
{
$analysis = array(strtolower(gettype($argument)));
switch ($analysis[0]) {
case 'object':
$analysis[] = strtolower(get_class($argument));
break;
case 'array':
if (array_merge($argument) == array_merge(array_values($argument))) { // numeric keys
$analysis[] = $count = count($argument);
if ($count > 0 && $argument == array_merge($argument)) { // keys starting with index 0, incrementing 1 per element
$analysis[] = 'i';
}
} else { // string keys (assoc array)
$keys = array_keys($argument);
sort($keys);
$analysis = array_merge($analysis, $keys);
}
break;
}
if (count($analysis) == 1) {
return $analysis[0];
} else {
return $analysis[0] . '(' . implode(',', array_slice($analysis, 1)) . ')';
}
}
}
?> |