Completed
Pull Request — master (#7)
by Greg
02:34
created

GherkinParam::getValueFromArray()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 25
rs 8.439
cc 6
eloc 18
nc 8
nop 1
1
<?php
2
/**
3
 * Before step hook that provide parameter syntax notation
4
 * for accessing fixture data between Gherkin steps/tests
5
 * example:
6
 *  I see "{{param}}"
7
 *  {{param}} will be replaced by the value of Fixtures::get('param')
8
 *
9
 */
10
namespace Codeception\Extension;
11
12
use RuntimeException;
13
use Codeception\Util\Fixtures;
14
use Behat\Gherkin\Node\TableNode;
15
use ReflectionProperty;
16
17
class GherkinParam extends \Codeception\Platform\Extension
18
{
19
  // list events to listen to
20
  public static $events = array(
21
    //run before any suite
22
    'suite.before' => 'beforeSuite',
23
    //run before any steps
24
    'step.before' => 'beforeStep'
25
  );
26
27
  private static $suite_config;
28
29
  private static $regEx = Array(
30
                          'match'  => '/^{{[A-z0-9_:-]+}}$/',
31
                          'filter' => '/[{}]/',
32
                          'config' => '/(?:^config)?:([A-z0-9_-]+)+(?=:|$)/',
33
                          'array'  => '/^(?P<var>[A-z0-9_-]+)(?:\[(?P<key>.+)])$/'
34
                        );
35
36
  // parse param and replace {{.*}} by its Fixtures::get() value if exists
37
  protected function getValueFromParam($param)
38
  {
39
    if (preg_match(static::$regEx['match'], $param)) {
1 ignored issue
show
Bug introduced by
Since $regEx is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $regEx to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
40
      $arg = preg_filter(static::$regEx['filter'], '', $param);
1 ignored issue
show
Bug introduced by
Since $regEx is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $regEx to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
41
      if (preg_match(static::$regEx['config'], $arg)) {
1 ignored issue
show
Bug introduced by
Since $regEx is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $regEx to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
42
        return $this->getValueFromConfig($arg);
43
      } elseif (preg_match(static::$regEx['array'], $arg)) {
1 ignored issue
show
Bug introduced by
Since $regEx is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $regEx to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
44
        return $this->getValueFromArray($arg);
45
      }else {
46
        return Fixtures::get($arg);
47
      }
48
    } else {
49
      return $param;
50
    }
51
  }
52
53
  protected function getValueFromConfig($param)
54
  {
55
    $value = null;
56
    $config = static::$suite_config;
1 ignored issue
show
Bug introduced by
Since $suite_config is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $suite_config to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
57
58
    preg_match_all(static::$regEx['config'], $param, $args, PREG_PATTERN_ORDER);
1 ignored issue
show
Bug introduced by
Since $regEx is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $regEx to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
59
    foreach ($args[1] as $arg) {
60
      if (array_key_exists($arg, $config)) {
61
        $value = $config[$arg];
62
        if (is_array($value)) {
63
          $config = $value;
64
        } else {
65
          break;
66
        }
67
      }
68
    }
69
    return $value;
70
  }
71
72
  protected function getValueFromArray($param)
73
  {
74
    $value = null;
75
76
    preg_match_all(static::$regEx['array'], $param, $args);
1 ignored issue
show
Bug introduced by
Since $regEx is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $regEx to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
77
    $array = Fixtures::get($args['var'][0]);
78
    foreach ($args['key'] as $arg) {
79
      do {
80
        $exist = array_key_exists($arg, $array);
81
          if ($exist) {
82
          $value = $array[$arg];
83
          if (is_array($value)) {
84
            $array = $value;
85
          } else {
86
            break;
87
          }
88
        }
89
      } while($exist);
90
    }
91
    if ($exist) {
0 ignored issues
show
Bug introduced by
The variable $exist does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
92
      return $value;
93
    } else {
94
      throw new RuntimeException("{$array}[{$arg}] does not exist");
95
    }
96
  }
97
98
  public function beforeSuite(\Codeception\Event\SuiteEvent $e)
99
  {
100
    static::$suite_config = $e->getSettings();
1 ignored issue
show
Bug introduced by
Since $suite_config is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $suite_config to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
101
  }
102
103
  public function beforeStep(\Codeception\Event\StepEvent $e)
104
  {
105
    $step = $e->getStep();
106
    // access to the protected property using reflection
107
    $refArgs = new ReflectionProperty(get_class($step), 'arguments');
108
    // change property accessibility to public
109
    $refArgs->setAccessible(true);
110
    // retrieve 'arguments' value
111
    $args = $refArgs->getValue($step);
112
    foreach ($args as $index => $arg) {
113
      if (is_string($arg)) {
114
      // case if arg is a string
115
      // e.g. I see "{{param}}"
116
        $args[$index] = $this->getValueFromParam($arg);
117
      } elseif (is_a($arg, '\Behat\Gherkin\Node\TableNode')) {
118
      // case if arg is a table
119
      // e.g. I see :
120
      //  | paramater |
121
      //  | {{param}} |
122
        $table = Array();
123
        foreach ($arg->getRows() as $i => $row) {
124
          foreach ($row as $j => $cell) {
125
              $table[$i][$j] = $this->getValueFromParam($cell);
126
          }
127
        }
128
        $args[$index] = new TableNode($table);
129
      }
130
    }
131
    // set new arguments value
132
    $refArgs->setValue($step, $args);
133
  }
134
135
}
136