Completed
Push — master ( 6ad205...f1739d )
by Chris
02:56
created

fs_utils::combine_paths()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Vendi\Shared;
4
5
final class fs_utils
6
{
7
8
    const CREATE_DIRECTORY_IF_NOT_EXISTS_OR_FAIL = true;
9
10
    const OK_IF_PATH_DOES_NOT_EXIST = false;
11
12
    /**
13
     * [create_random_temp_dir description]
14
     * @param  string        $prefix    [description]
15
     * @param  callable|null $rand_func [description]
16
     * @return [type]                   [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
17
     */
18 3
    public static function create_random_temp_dir( string $prefix, callable $rand_func = null ) : string
19
    {
20 3
        if( ! $prefix )
21
        {
22 1
            throw new utils_exception( 'You must provide a prefix when calling create_random_temp_dir()' );
23
        }
24
25 2
        $prefix = trim( $prefix, '_' ) . '_';
26
27 2
        if( ! $rand_func )
28
        {
29 1
            $rand_func = function( $prefix )
30
                         {
31 1
                            return uniqid( $prefix, true );
32 1
                        };
33
        }
34
35 2
        $tmp_folder = sys_get_temp_dir();
36 2
        while( true )
37
        {
38 2
            $sub_folder = $rand_func( $prefix );
39
40
            //Normally we'd want combine_paths to make the directory for us but
41
            //in this case we only want to return if it doesn't already exist.
42 2
            $full_path = self::combine_paths( self::OK_IF_PATH_DOES_NOT_EXIST, $tmp_folder, $sub_folder );
43
44
            //The directory exists, loop again
45 2
            if( is_dir( $full_path ) )
46
            {
47 1
                continue;
48
            }
49
50
            //Make the directory.
51
            //NOTE: This method throws if it cannot create the directory.
52 2
            self::mkdir( $full_path );
53
54 2
            break;
55
        }
56
57
        //Return this outside of the while loop so that code coverage can handle
58
        //the full function
59 2
        return $full_path;
0 ignored issues
show
Bug introduced by
The variable $full_path 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...
60
    }
61
62 7
    public static function combine_paths_with_file( bool $create, string $file, string ...$path_parts )
63
    {
64 7
        $ret = '/';
65
66 7
        foreach( $path_parts as $pp )
67
        {
68 6
            $ret .= trim( $pp, '/\\' ) . '/';
69
70 6
            if( $create )
71
            {
72 6
                self::mkdir( $ret );
73
            }
74
        }
75
76 7
        if( $file )
77
        {
78 7
            $ret .= trim( $file, '/\\' );
79
        }
80
81 7
        return $ret;
82
    }
83
84 7
    public static function combine_paths( bool $create, string ...$path_parts )
85
    {
86 7
        return self::combine_paths_with_file( $create, '', ...$path_parts );
87
    }
88
89
    /**
90
     * Create the given path if it doesn't exist.
91
     *
92
     * NOTE: This method does not test user permissions to write to the
93
     * directory, that must be handled by callers.
94
     *
95
     * @param  string $path    The absolute path
96
     * @param  string $context An optional context to help explain the mkdir
0 ignored issues
show
Documentation introduced by
Should the type for parameter $context not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
97
     *                         conditions for debugging.
98
     * @return true            Returns true if successful, otherwise an
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
99
     *                         exception is thrown.
100
     */
101 7
    public static function mkdir( string $path, string $context = null ) : bool
102
    {
103
        //Make sure we've got something to work with.
104 7
        if( ! $path || ! trim( $path ) )
105
        {
106 2
            throw new utils_exception( 'You must provide a path when calling mkdir.' );
107
        }
108
109
        //Make sure the path is absolute
110 5
        if( 0 !== strpos( $path, '/' ) )
111
        {
112 1
            throw new utils_exception( 'You must provide an absolute path when calling mkdir.' );
113
        }
114
115
        //Does the directory already exist?
116 4
        if( is_dir( $path ) )
117
        {
118 1
            return true;
119
        }
120
121
        //Try to make it
122 3
        @mkdir( $path );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
123
124
        //mkdir fails if the directory already exists however if this
125
        //command is executed simultaneously by two people there could be a
126
        //race condition. So before failing hard, see if someone else
127
        //actually succeeded in making in.
128 3
        if( is_dir( $path ) )
129
        {
130 1
            return true;
131
        }
132
133 2
        if( $context )
0 ignored issues
show
Bug Best Practice introduced by
The expression $context of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
134
        {
135 1
            $msg = sprintf( 'Cannot create directory %1$s for context %2$s.', $path, $context );
136
        }
137
        else
138
        {
139 1
            $msg = sprintf( 'Cannot create directory %1$s.', $path, $context );
140
        }
141
142 2
        throw new utils_exception( $msg );
143
    }
144
}
145