title | type | order |
---|---|---|
Mutators |
guide |
5 |
Infection supports a set of Mutators which are based on AST and PHP-Parser project.
Read more about how to execute only particular set of mutators using
Name
column
Name | Original | Mutated |
---|---|---|
PublicVisibility | public function ... |
protected function ... |
ProtectedVisibility | protected function ... |
private function ... |
To verify that the visibility of a method is necessary. If the visibility of a method can be reduced from public
to protected
or private
, this may be an indication that the publicly accessible part of API of a class is larger than what’s strictly necessary. This mutator will drive the source code towards classes with smaller publicly accessible APIs and thus better encapsulation.
The Unwrap* mutator family will unwrap function parameters.
Name | Original | Mutated |
---|---|---|
UnwrapArrayChangeKeyCase | $a = array_change_key_case(['foo' => 'bar']); |
$a = ['foo' => 'bar']; |
UnwrapArrayChunk | $a = array_chunk(['A', 'B', 'C'], 2); |
$a = ['A', 'B', 'C']; |
UnwrapArrayColumn | $a = array_column([['foo' => 'bar]], 'foo'); |
$a = [['foo' => 'bar]]; |
UnwrapArrayCombine | $a = array_combine(['A', 'B', 'C'], ['foo', 'bar', 'baz']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayDiff | $a = array_diff(['A', 'B', 'C'], ['D']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayDiffAssoc | $a = array_diff_assoc(['foo' => 'bar'], ['baz' => 'bar]); |
$a = ['foo' => 'bar']; |
UnwrapArrayDiffKey | $a = array_diff_key(['foo' => 'bar'], ['baz' => 'bar]); |
$a = ['foo' => 'bar']; |
UnwrapArrayDiffUassoc | $a = array_diff_assoc(['foo' => 'bar'], ['baz' => 'bar], $keyCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayDiffUkey | $a = array_diff_ukey(['foo' => 'bar'], ['baz' => 'bar], $keyCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayFilter | $a = array_filter(['A', 1, 'C'], 'is_int'); |
$a = ['A', 1, 'C']; |
UnwrapArrayFlip | $a = array_flip(['A', 'B', 'C']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayIntersect | $a = array_intersect(['A', 'B', 'C'], ['D']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayIntersectKey | $a = array_intersect_key(['foo' => 'bar'], ['bar' => 'baz']); |
$a = ['foo' => 'bar']; |
UnwrapArrayIntersectUassoc | $a = array_intersect_uassoc(['foo' => 'bar'], ['bar' => 'baz'], $keyCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayIntersectUkey | $a = array_intersect_ukey(['foo' => 'bar'], ['bar' => 'baz'], $keyCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayKeys | $a = array_keys(['foo' => 'bar']); |
$a = ['foo' => 'bar']; |
UnwrapArrayMap | $a = array_map('strtolower', ['A', 'B', 'C']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayMerge | $a = array_merge(['A', 'B', 'C'], ['D']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayMergeRecursive | $a = array_merge_recursive(['A', 'B', 'C'], ['D']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayReduce | $a = array_reduce(['A', 'B', 'C'], $callback, ['D']); |
$a = ['D']; |
UnwrapArrayReplace | $a = array_replace(['A', 'B', 'C'], ['D']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayReplaceRecursive | $a = array_replace_recursive(['A', 'B', 'C'], ['D']); |
$a = ['A', 'B', 'C']; |
UnwrapArrayReverse | $a = array_reverse(['A', 'B', 'C']); |
$a = ['A', 'B', 'C']; |
UnwrapArraySlice | $a = array_slice(['A', 'B', 'C'], 1); |
$a = ['A', 'B', 'C']; |
UnwrapArraySplice | $a = array_splice(['A', 'B', 'C'], 1); |
$a = ['A', 'B', 'C']; |
UnwrapArrayUdiff | $a = array_udiff(['foo' => 'bar'], ['baz' => 'bar], $valueCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayUdiffAssoc | $a = array_udiff_assoc(['foo' => 'bar'], ['baz' => 'bar], $valueCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayUdiffUassoc | $a = array_udiff_uassoc(['foo' => 'bar'], ['baz' => 'bar], $valueCompareFunc, $keyCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayUintersect | $a = array_uintersect(['foo' => 'bar'], ['baz' => 'bar], $valueCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayUintersectAssoc | $a = array_uintersect_assoc(['foo' => 'bar'], ['baz' => 'bar], $valueCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayUintersectUassoc | $a = array_uintersect_uassoc(['foo' => 'bar'], ['baz' => 'bar], $valueCompareFunc, $keyCompareFunc); |
$a = ['foo' => 'bar']; |
UnwrapArrayUnique | $a = array_unique(['foo', 'bar', 'bar']); |
$a = ['foo', 'bar', 'bar']; |
UnwrapArrayValues | $a = array_values(['foo' => 'bar']); |
$a = ['foo' => 'bar']; |
UnwrapLcFirst | $a = lcfirst('Hello, world!'); |
$a = 'Hello, world!'; |
UnwrapStrRepeat | $a = str_repeat('A', 3); |
$a = 'A'; |
UnwrapStrToLower | $a = strtolower('Hello!'); |
$a = 'Hello!'; |
UnwrapStrToUpper | $a = strtoupper('Hello, world!'); |
$a = 'Hello, world!'; |
UnwrapTrim | $a = trim(' Hello, world! '); |
$a = 'Hello, world!'; |
UnwrapUcFirst | $a = ucfirst('hello, world!'); |
$a = 'hello, world!'; |
UnwrapUcWords | $a = ucwords('hello, world!'); |
$a = 'hello, world!'; |
Name | Original | Mutated |
---|---|---|
Plus | + | - |
Minus | - | + |
Multiplication | * | / |
Division | / | * |
Modulus | % | * |
Exponentiation | ** | / |
MulEqual | *= | /= |
PlusEqual | += | -= |
MinusEqual | -= | += |
DivEqual | /= | *= |
ModEqual | %= | *= |
PowEqual | **= | /= |
BitwiseAnd | & | | |
BitwiseOr | | | & |
BitwiseXor | ^ | & |
BitwiseNot | ~ | |
ShiftRight | >> | << |
ShiftLeft | << | >> |
AssignmentEqual | == | = |
Assignment | += | = |
Assignment | -= | = |
Assignment | *= | = |
Assignment | **= | = |
Assignment | /= | = |
Assignment | %= | = |
Assignment | .= | = |
Assignment | &= | = |
Assignment | |= | = |
Assignment | ^= | = |
Assignment | <<= | = |
Assignment | >>= | = |
Assignment Coalesce | ??= | = |
The Round Family mutator will make sure that there's enough tests to cover the rounding possibilities.
Name | Original | Mutated |
---|---|---|
RoundingFamily | round() |
floor() |
RoundingFamily | round() |
ceil() |
RoundingFamily | ceil() |
floor() |
RoundingFamily | ceil() |
round() |
RoundingFamily | floor() |
round() |
RoundingFamily | floor() |
ceil() |
Name | Original | Mutated |
---|---|---|
ArrayItem | [$a->foo => $b->bar] |
[$a->foo > $b->bar] |
TrueValue | true | false |
FalseValue | false | true |
LogicalAnd | && | || |
LogicalOr | || | && |
LogicalLowerAnd | and | or |
LogicalLowerOr | or | and |
LogicalNot | ! | |
Yield_ | yield $a => $b; |
yield $a > $b; |
Coalesce | $a ?? $b |
$b |
Default settings:
in_array: false
: whether to mutate 3rd argumenttrue
in function callarray_search: false
: whether to mutate 3rd argumenttrue
in function call
infection.json:
{
"mutators": {
"TrueValue": {
"settings": {
"in_array": true,
"array_search": true
}
}
}
}
Name | Original | Mutated |
---|---|---|
GreaterThan | > | >= |
LessThan | < | <= |
GreaterThanOrEqualTo | >= | > |
LessThanOrEqualTo | <= | < |
Name | Original | Mutated |
---|---|---|
EqualIdentical | == |
=== |
NotEqualNotIdentical | != |
!== |
IdenticalEqual | === |
== |
NotIdenticalNotEqual | !== |
!= |
These mutators are disabled by default, you can use the
@equal
or@identical
profiles to enable the ones you prefer.
Name | Original | Mutated |
---|---|---|
Equal | == | != |
NotEqual | != | == |
Identical | === | !== |
NotIdentical | !== | === |
GreaterThanNegotiation | > | <= |
LessThanNegotiation | < | >= |
GreaterThanOrEqualToNegotiation | >= | < |
LessThanOrEqualToNegotiation | <= | > |
Name | Original | Mutated |
---|---|---|
Increment | ++ | -- |
Decrement | -- | ++ |
Name | Original | Mutated |
---|---|---|
TrueValue | return true; | return false; |
FalseValue | return false; | return true; |
OneZeroInteger | return 0; | return 1; |
IntegerNegation | return (Any Integer) ; |
return -(Any Integer) ; |
OneZeroFloat | return 0.0; | return 1.0; |
OneZeroFloat | return 1.0; | return 0.0; |
FloatNegation | return (Any Float) ; |
return -(Any Float) ; |
This | return $this; | return null; |
FunctionCall | return function(); | function(); return null; |
NewObject | return new Class(); | new Class(); return null; |
Name | Original | Mutated |
---|---|---|
ArrayItemRemoval | [1, $a, '3'] |
[$a, '3'] depending on configuration |
FunctionCallRemoval | foo_bar($a) | - |
MethodCallRemoval | $this->method($var) | - |
Configuration options:
remove: first
: defines the way the mutator operates. Could be:first
- remove only first element from each arraylast
- remove only last element from each arrayall
- remove every element one by one from each array resulting in as many mutations as total number of items in arrays.
limit: PHP_INT_MAX
: whenremove = all
specifies maximum number of elements that will be removed form array. Only elements at the beginning will be mutated.
When using
all
option we advise to set the limit as well
You should remember to exclude files containing large arrays (like configuration) when using
ArrayItemRemoval
mutator inall
mode
infection.json:
{
"mutators": {
"ArrayItemRemoval": {
"settings": {
"remove": "all",
"limit": 15
}
}
}
}
Name | Original | Mutated |
---|---|---|
Break_ | break; | continue; |
Continue_ | continue; | break; |
Foreach_ | foreach ($someVar as ...); | foreach ([] as ...); |
For_ | for ($i=0; $i < 10; $i++); | for ($i=0; false; $i++); |
Name | Original | Mutated |
---|---|---|
Spaceship | $a <=> $b | $b <=> $a |
Name | Original | Mutated |
---|---|---|
OneZeroInteger | 0 | 1 |
OneZeroInteger | 1 | 0 |
OneZeroFloat | 0.0 | 1.0 |
OneZeroFloat | 1.0 | 0.0 |
DecrementInteger | 7 | 6 |
IncrementInteger | 7 | 8 |
Name | Original | Mutated |
---|---|---|
Throw_ | throw new NotFoundException(); |
new NotFoundException(); |
Finally_ | try {} catch (\Exception $e) {} finally {} |
try {} catch (\Exception $e) {} |
Name | Original | Mutated |
---|---|---|
CastArray | (array) $value; |
$value |
CastBool | (bool) $value; |
$value |
CastFloat | (float) $value; |
$value |
CastInt | (int) $value; |
$value |
CastObject | (object) $value; |
$value |
CastString | (string) $value; |
$value |
Name | Original | Mutated |
---|---|---|
PregQuote | $a = preg_quote('text'); |
$a = 'text'; |
PregMatchMatches | preg_match('/pattern/', $value, $matches); |
(int) $matches = array(); |
Name | Original | Mutated |
---|---|---|
MBString | mb_chr($code); |
chr($code); |
mb_ord($character); |
ord($character); |
|
mb_parse_str('text', $results); |
parse_str('text', $results); |
|
mb_send_mail($to, $subject, $message, $headers, $parameters); |
mail($to, $subject, $message, $headers, $parameters); |
|
mb_strcut('text', 0, 123, 'utf-8'); |
substr('text', 0, 123); |
|
mb_stripos('text', 't', 0, 'utf-8'); |
stripos('text', 't', 0); |
|
mb_stristr('text', 't', true, 'utf-8'); |
stristr('text', 't', true); |
|
mb_strlen('text', 'utf-8'); |
strlen('text'); |
|
mb_strpos('text', 't', 0, 'utf-8'); |
strpos('text', 't', 0); |
|
mb_strrchr('text', 't', true, 'utf-8'); |
strrchr('text', 't', true); |
|
mb_strripos('text', 't', 0, 'utf-8'); |
strripos('text', 't', 0); |
|
mb_strrpos('text', 't', 0, 'utf-8'); |
strrpos('text', 't', 0); |
|
mb_strstr('text', 't', true, 'utf-8'); |
strstr('text', 't', true); |
|
mb_strtolower('text', 'utf-8'); |
strtolower('text'); |
|
mb_strtoupper('text', 'utf-8'); |
strtoupper('text'); |
|
mb_substr_count('text', 't', 'utf-8'); |
substr_count('text', 't'); |
|
mb_substr('text', 0, 123, 'utf-8'); |
substr('text', 0, 123); |
|
mb_convert_case('text', $mode); |
strtoupper('text'); , strtolower('text'); or ucwords('text'); depending on mode |
"mb_parse_str": true
: You are able to disable any of the supported mb string functions. All supported functions are enabled by default.
Some of the functions are not supported due to complexity to convert them to standard string manipulation functions. Implementing them either does not make sense or creates too many false positive mutations. Not supported functions are
mb_ereg*
,mb_split
,mb_strrichr
,mb_get_info
and similar.
infection.json:
{
"mutators": {
"MBString": {
"settings": {
"mb_send_mail": false,
"mb_substr_count": false
}
}
}
}