Completed
Push — master ( cb9cb0...a2a2e1 )
by
unknown
43:00 queued 13:11
created

Log   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 324
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 71.33%

Importance

Changes 15
Bugs 1 Features 13
Metric Value
wmc 56
lcom 1
cbo 2
dl 0
loc 324
ccs 102
cts 143
cp 0.7133
rs 6.5957
c 15
b 1
f 13

20 Methods

Rating   Name   Duplication   Size   Complexity  
A addDelimiterFwdSlash() 0 4 1
B maskSensitiveXmlString() 0 26 4
A maskCreditCards() 0 15 2
A getPropertiesInclBase() 0 14 4
B checkPropertyAndMask() 0 23 5
B maskSensitiveProperties() 0 32 6
B getMasked() 0 27 5
B log() 0 21 7
A debug() 0 6 2
A info() 0 5 2
A warn() 0 5 2
A error() 0 5 2
A logFormat() 0 12 3
A debugFormat() 0 6 2
A infoFormat() 0 5 2
A warnFormat() 0 5 2
A errorFormat() 0 5 2
A setLogFile() 0 3 1
A getLogFile() 0 3 1
A __construct() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Log often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Log, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace net\authorize\util;
3
4
use net\authorize\util\ANetSensitiveFields;
5
6 1
define ("ANET_LOG_FILES_APPEND",true);
7 1
8
define("ANET_LOG_DEBUG_PREFIX","DEBUG");
9 1
define("ANET_LOG_INFO_PREFIX","INFO");
10 1
define("ANET_LOG_WARN_PREFIX","WARN");
11 1
define("ANET_LOG_ERROR_PREFIX","ERROR");
12 1
13
//log levels
14
define('ANET_LOG_DEBUG',1);
15 1
define("ANET_LOG_INFO",2);
16 1
define("ANET_LOG_WARN",3);
17 1
define("ANET_LOG_ERROR",4);
18 1
19
//set level
20
define("ANET_LOG_LEVEL",ANET_LOG_DEBUG);
21 1
22
/**
23
 * A class to implement logging.
24
 *
25
 * @package    AuthorizeNet
26
 * @subpackage net\authorize\util
27
 */
28
29
class Log
30
{
31
    private $sensitiveXmlTags = NULL;
32
    private $logFile = '';
33
	
34
	/**
35
	* Takes a regex pattern (string) as argument and adds the forward slash delimiter.
36
	* Also adds the u flag to enable Unicode mode regex.
37
	*
38
	* @param string $regexPattern
39
	*
40
	* @return string
41
	*/
42 8
	private function addDelimiterFwdSlash($regexPattern)
43
	{
44 8
		return '/'.$regexPattern.'/u';
45
	}
46
	
47
	/**
48
	* Takes an xml as string and masks the sensitive fields.
49
	*
50
	* @param string $rawString		The xml as a string.
51
	*
52
	* @return string 		The xml as a string after masking sensitive fields
53
	*/
54 8
    private function maskSensitiveXmlString($rawString){
55 8
        $patterns=array();
56 8
        $replacements=array();
57
		
58 8
        foreach ($this->sensitiveXmlTags as $i => $sensitiveTag){
59 8
            $tag = $sensitiveTag->tagName;
60 8
            $inputPattern = "(.+)"; //no need to mask null data
61 8
            $inputReplacement = "xxxx";
62
63 8
            if(trim($sensitiveTag->pattern)) {
64 8
                $inputPattern = $sensitiveTag->pattern;
65
            }
66 8
            $pattern = "<" . $tag . ">(?:.*)". $inputPattern ."(?:.*)<\/" . $tag . ">";
67 8
			$pattern = $this->addDelimiterFwdSlash($pattern);
68
69 8
            if(trim($sensitiveTag->replacement)) {
70 8
                $inputReplacement = $sensitiveTag->replacement;
71
            }
72 8
            $replacement = "<" . $tag . ">" . $inputReplacement . "</" . $tag . ">";
73
74 8
            $patterns [$i] = $pattern;
75 8
            $replacements[$i]  = $replacement;
76
        }
77 8
        $maskedString = preg_replace($patterns, $replacements, $rawString);
78 8
        return $maskedString;
79
    }
80
81
    /**
82
     * Takes a string and masks credit card regex matching parts.
83
     *
84
     * @param string $rawString		The string.
85
     *
86
     * @return string 		The string after masking credit card regex matching parts.
87
     */
88 8
    private function maskCreditCards($rawString){
89 8
        $patterns=array();
90 8
        $replacements=array();
91
92 8
        foreach ($this->sensitiveStringRegexes as $i => $creditCardRegex){
0 ignored issues
show
Bug introduced by
The property sensitiveStringRegexes does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
93 8
            $pattern = $creditCardRegex;
94 8
			$pattern = $this->addDelimiterFwdSlash($pattern);
95
96 8
            $replacement = "xxxx";
97 8
            $patterns [$i] = $pattern;
98 8
            $replacements[$i]  = $replacement;
99
        }
100 8
        $maskedString = preg_replace($patterns, $replacements, $rawString);
101 8
        return $maskedString;
102
    }
103
	
104
	/**
105
	* Object data masking related functions START
106
	*/
107
	
108
	/**
109
	* private function getPropertiesInclBase($reflClass).
110
	* 
111
	* Receives a ReflectionObject, ...
112
	* iteratively fetches the properties of the object (including from the base classes up the hierarchy), ...
113
	* collects them in an array of ReflectionProperty and returns the array.
114
	*
115
	* @param ReflectionObject $reflClass
116
	*
117
	* @return \ReflectionProperty[]
118
	*/
119 8
	private function getPropertiesInclBase($reflClass)
120
	{
121 8
		$properties = array();
122
		try {
123
			do {
124 8
				$curClassPropList = $reflClass->getProperties();
125 8
				foreach ($curClassPropList as $p) {
126 8
					$p->setAccessible(true);
127
				}
128 8
				$properties = array_merge($curClassPropList, $properties);
129 8
			} while ($reflClass = $reflClass->getParentClass());
130
		} catch (\ReflectionException $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
131 8
		return $properties;
132
	}
133
	
134
	/**
135
	* private function checkPropertyAndMask($prop, $obj).
136
	* 
137
	* Receives a ReflectionProperty and an object, and returns a masked object if the ReflectionProperty corresponds to a sensitive field, else returns false.
138
	*
139
	* @param ReflectionProperty $prop
140
	* @param object $obj
141
	*
142
	* @return string|bool
143
	*/
144 8
	private function checkPropertyAndMask($prop, $obj){
145 8
		foreach($this->sensitiveXmlTags as $i => $sensitiveField)
146
		{
147 8
			$inputPattern = "(.+)";
148 8
			$inputReplacement = "xxxx";
149
150 8
            if(trim($sensitiveField->pattern)) {
151 8
                $inputPattern = $sensitiveField->pattern;
152
            }
153 8
			$inputPattern = $this->addDelimiterFwdSlash($inputPattern);
154
			
155 8
            if(trim($sensitiveField->replacement)) {
156 8
                $inputReplacement = $sensitiveField->replacement;
157
            }
158
			
159 8
			if(strcmp($prop->getName(),$sensitiveField->tagName)==0)
160
			{
161 8
				$prop->setValue($obj,preg_replace($inputPattern,$inputReplacement,$prop->getValue($obj)));
162 8
				return $prop->getValue($obj);
163
			}
164
		}
165 8
		return false;
166
	}
167
	
168
	/**
169
	* called by getMasked() to mask sensitive fields of an object.
170
	*
171
	* @param object $obj
172
	*
173
	* @return object
174
	*/
175 8
    private function maskSensitiveProperties ($obj)
176
    {
177
		// first retrieve all properties of the passed object
178 8
        $reflectObj = new \ReflectionObject($obj);
179 8
        $props = $this->getPropertiesInclBase($reflectObj);
0 ignored issues
show
Documentation introduced by
$reflectObj is of type object<ReflectionObject>, but the function expects a object<net\authorize\util\ReflectionObject>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
180
181
		// for composite property recursively execute; for scalars, do a check and mask
182 8
        foreach($props as $i => $prop){
183 8
			$propValue=$prop->getValue($obj);
184
			
185
			// for object and arrays, recursively call for inner elements
186 8
			if(is_object($propValue)){
187 8
				$prop->setValue($obj, $this->maskSensitiveProperties($propValue));
188
            }
189 8
			else if(is_array($propValue)){
190
				$newVals=array();
191
				foreach($propValue as $i=>$arrEle)
192
				{
193
					$newVals[]=$this->maskSensitiveProperties($arrEle);
194
				}
195
				$prop->setValue($obj, $newVals);
196
            }
197
			// else check if the property represents a sensitive field. If so, mask.
198
            else{
199 8
				$res=$this->checkPropertyAndMask($prop, $obj);
0 ignored issues
show
Documentation introduced by
$prop is of type object<ReflectionProperty>, but the function expects a object<net\authorize\util\ReflectionProperty>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
200 8
				if($res)
201 8
					$prop->setValue($obj, $res);
202
            }
203
        }
204
		
205 8
        return $obj;
206
    }
207
	
208
	/**
209
	* Object data masking related functions END
210
	*/
211
	
212
	/**
213
	* private function getMasked($raw).
214
	*
215
	* called by log()
216
	*
217
	* @param mixed $raw
218
	*
219
	* @return string
220
	*/
221 8
    private function getMasked($raw)
222
    { //always returns string
223 8
        $messageType = gettype($raw);
224 8
        $message="";
0 ignored issues
show
Unused Code introduced by
$message is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
225 8
        if($messageType == "object"){
226 8
			$obj = unserialize(serialize($raw)); // deep copying the object
227 8
			$message = print_r($this->maskSensitiveProperties($obj), true); //object to string
228
        }
229 8
        else if($messageType == "array"){
230
            $copyArray = unserialize(serialize($raw));
231
            foreach($copyArray as $i => $element){
232
                $copyArray[$i] = $this->getMasked($element);
233
            }
234
            $message = print_r($copyArray, true); // returns string
235
        }
236
        else { //$messageType == "string")
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
237 8
            $primtiveTypeAsString = strval($raw);
238
239 8
            $maskedXml = $primtiveTypeAsString;
240 8
            if($messageType == "string") {
241 8
                $maskedXml = $this->maskSensitiveXmlString($primtiveTypeAsString);
242
            }
243
            //mask credit card numbers
244 8
            $message = $this->maskCreditCards($maskedXml);
245
        }
246 8
        return $message;
247
    }
248
	
249 8
	private function log($logLevelPrefix, $logMessage, $flags){
250
        if (!$this->logFile) return;
251 8
        //masking
252
        $logMessage = $this->getMasked($logMessage);
253
254 8
        //debug_backtrace
255 8
        $fileName = 'n/a';
256 8
        $methodName = 'n/a';
257 8
        $lineNumber = 'n/a';
258 8
        $debugTrace = debug_backtrace();
259 8
        if (isset($debugTrace[1])) {
260 8
            $fileName = $debugTrace[1]['file'] ? $debugTrace[1]['file'] : 'n/a';
261
            $lineNumber = $debugTrace[1]['line'] ? $debugTrace[1]['line'] : 'n/a';
262 8
        }
263
        if (isset($debugTrace[2])) $methodName = $debugTrace[2]['function'] ? $debugTrace[2]['function'] : 'n/a';
264
265 8
        //Add timestamp, log level, method, file, line
266
        $logString = sprintf("\n %s %s : [%s] (%s : %s) - %s", \net\authorize\util\Helpers::now(), $logLevelPrefix,
267 8
            $methodName, $fileName, $lineNumber, $logMessage);
268 8
        file_put_contents($this->logFile, $logString, $flags);
269
    }
270 8
	
271
    public function debug($logMessage, $flags=FILE_APPEND)
272 8
    {
273 8
        if(ANET_LOG_DEBUG >= ANET_LOG_LEVEL){
274
            $this->log(ANET_LOG_DEBUG_PREFIX, $logMessage,$flags);
275 8
        }
276
    }
277 8
	
278 8
    public function info($logMessage, $flags=FILE_APPEND){
279 8
        if(ANET_LOG_INFO >= ANET_LOG_LEVEL) {
280
            $this->log(ANET_LOG_INFO_PREFIX, $logMessage,$flags);
281 8
        }
282
    }
283
	
284
	public function warn($logMessage, $flags=FILE_APPEND){
285
        if(ANET_LOG_WARN >= ANET_LOG_LEVEL) {
286
            $this->log(ANET_LOG_WARN_PREFIX, $logMessage,$flags);
287
        }
288
    }
289
	
290
    public function error($logMessage, $flags=FILE_APPEND){
291
        if(ANET_LOG_ERROR >= ANET_LOG_LEVEL) {
292
            $this->log(ANET_LOG_ERROR_PREFIX, $logMessage,$flags);
293
        }
294
    }
295
	
296
	private function logFormat($logLevelPrefix, $format, $objects, $flags){
297
        try {
298
            foreach($objects as $i => $testObject){
299
                $objects[$i] = $this->getMasked($testObject);
300
            }
301
            $logMessage = vsprintf($format, $objects);
302
            $this->log($logLevelPrefix, $logMessage, $flags);
303
        }
304
        catch(\Exception $e){
305
            $this->debug("Incorrect log message format: " . $e->getMessage());
306
        }
307
    }
308
	
309
	public function debugFormat($format, $args=array(),  $flags=FILE_APPEND)
310
    {
311
        if(ANET_LOG_DEBUG >= ANET_LOG_LEVEL){
312
            $this->logFormat(ANET_LOG_DEBUG_PREFIX, $format, $args , $flags);
313
        }
314
    }
315
	
316
	public function infoFormat($format, $args=array(),  $flags=FILE_APPEND){
317
        if(ANET_LOG_INFO >= ANET_LOG_LEVEL) {
318
            $this->logFormat(ANET_LOG_INFO_PREFIX, $format, $args , $flags);
319
        }
320
    }
321
	
322
	public function warnFormat($format, $args=array(),  $flags=FILE_APPEND){
323
        if(ANET_LOG_WARN >= ANET_LOG_LEVEL) {
324
            $this->logFormat(ANET_LOG_WARN_PREFIX, $format, $args , $flags);
325
        }
326
    }
327
	
328
    public function errorFormat($format, $args=array(),  $flags=FILE_APPEND){
329
        if(ANET_LOG_ERROR >= ANET_LOG_LEVEL) {
330
			$this->logFormat(ANET_LOG_ERROR_PREFIX, $format, $args , $flags);
331
        }
332
    }
333 1
334 1
    /**
335 1
     * @param string $logFile
336 1
     */
337
    public function setLogFile($logFile){
338
        $this->logFile = $logFile;
339
    }
340
341
    /**
342
     * @return string
343
     */
344
    public function getLogFile(){
345
        return $this->logFile;
346
    }
347
	
348
    public function __construct(){
349
        $this->sensitiveXmlTags = ANetSensitiveFields::getSensitiveXmlTags();
350
        $this->sensitiveStringRegexes = ANetSensitiveFields::getSensitiveStringRegexes();
351
    }
352
}
353
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...