Issues (65)

src/CruditesExceptionFactory.php (2 issues)

Labels
Severity
1
<?php
2
3
namespace HexMakina\Crudites;
4
5
use HexMakina\BlackBox\Database\QueryInterface;
6
7
/**
8
 * The CruditesExceptionFactory class make CruditesExcepton, a custom exception class that extends the built-in \Exception class in PHP. 
9
 * The purpose of this class is to provide a more descriptive and meaningful error message when an exception is thrown in the CRUD operations. 
10
 * It takes a message, an optional error code, and an optional previous exception as its parameters. 
11
 * It also provides a method called transcript() which extracts error information from a QueryInterface object 
12
 * and sets the error message to a more readable and understandable format. 
13
 * 
14
 * This is useful when dealing with database errors that may not be very clear or descriptive on their own.
15
 */
16
17
class CruditesExceptionFactory
18
{
19
    public static function make(QueryInterface $query, \PDOException $exception = null): CruditesException
20
    {
21
        $errorInfo = null;
22
23
        if (!$query->isSuccess())
24
            $errorInfo = $query->errorInfo();
0 ignored issues
show
The method errorInfo() does not exist on HexMakina\BlackBox\Database\QueryInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

24
            /** @scrutinizer ignore-call */ 
25
            $errorInfo = $query->errorInfo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
25
        elseif ($exception !== null)
26
            $errorInfo = $exception->errorInfo;
27
28
        if (!is_array($errorInfo))
29
            return new CruditesException('ERROR_INFO_UNAVAILABLE', 0, $exception);
30
31
        list($message, $code) = self::transcript($errorInfo);
32
33
        // TODO: losing the parsing work from transcript. IMPROVE
34
        if (is_array($message))
35
            $message = array_shift($message);
36
37
        return new CruditesException($message, $code, null); // exception could reveal database credentials
38
    }
39
40
    /**
41
     * Attempts to humanize database errors
42
     * @param ?array errorInfo, consists of at least the following fields:
0 ignored issues
show
The type HexMakina\Crudites\errorInfo was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
43
     *      0	SQLSTATE error code (a five characters alphanumeric identifier defined in the ANSI SQL standard).
44
     *      1	Driver-specific error code.
45
     *      2	Driver-specific error message.
46
     *
47
     * 
48
     * @return array consists of the following fields:
49
     *      0   the transcripted error message.
50
     *      1   the error code
51
     * 
52
     */
53
    private static function transcript(array $errorInfo): array
54
    {
55
        list($state, $code, $message) = $errorInfo;
56
        $functs = [
57
            // violation: column cannot be null
58
            1048 => function ($message) {
59
                preg_match("#Column '(.+)' cannot be null#", $message, $m);
60
                return ['FIELD_REQUIRED', $m[1]];
61
            },
62
63
            1054 => function ($message) {
64
                return ['COLUMN_DOES_NOT_EXIST', $message];
65
            },
66
67
            // violation: duplicate key
68
            1062 => function ($message) {
69
                if (preg_match("#'([^']+)' for key '([^']+)'#", $message, $m)) {
70
                    $entry = $m[1];
71
                    $key = $m[2];
72
                    return ["DUPLICATE_KEY:$key:$entry"];
73
                }
74
                
75
                if (preg_match("#for key '[a-z]+\.(.+)'$#", $message, $m) !== 1) {
76
                    preg_match("#for key '(.+)'$#", $message, $m);
77
                }
78
79
                return ["DUPLICATE_KEY:".$m[1]];
80
            },
81
82
            1064 => function ($message) {
83
                preg_match("#right syntax to use near '(.+)'#", $message, $m);
84
                return ['SYNTAX_ERROR', $m[1]];
85
            },
86
87
            1146 => function ($message) {
88
                return ['TABLE_DOES_NOT_EXIST', $message];
89
            },
90
91
            1264 => function ($message) {
92
                preg_match("#for column '(.+)'#", $message, $m);
93
                return ['VALUE_OUT_OF_RANGE', $m[1]];
94
            },
95
96
            1364 => function ($message) {
97
                return ['FIELD_REQUIRED', $message];
98
            },
99
100
            1451 => function ($message) {
101
                preg_match("#CONSTRAINT `(.+)` FOREIGN#", $message, $m);
102
                return ['RELATIONAL_INTEGRITY', $m[1]];
103
            },
104
105
        ];
106
        if (isset($functs[$code]))
107
            return [call_user_func($functs[$code], $message), $code];
108
109
        return ['FUBAR #' . $state . '-' . $code . '-' . $message, $code];
110
    }
111
}
112