Collection of mostly command line tools / PHP scripts. Somewhat out of date.

⌈⌋ ⎇ branch:  scripts + snippets


Check-in [5eaf0c63f6]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Transform PHP function parameter /*type*/ hints into assert() statements.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5eaf0c63f6f153df215883c0aa6abcebae0ada2f
User & Date: mario 2015-03-08 18:29:08
Context
2018-01-27
10:40
Add support for clearsigned `InRelease` file. check-in: 66b1ddf665 user: mario tags: trunk
2015-03-08
18:29
Transform PHP function parameter /*type*/ hints into assert() statements. check-in: 5eaf0c63f6 user: mario tags: trunk
2014-12-13
17:36
Retrieve count of backlinks to URL from various web services (Facebook, Google+, Twitter, Reddit, StumbleUpon, LinkedIn, Delicious, Pinterest) <em>Shallow merge of previous <code>php-scripts.fossil</code> repo.</em> check-in: c49ea11126 user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added php-assert-hints.php.































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/usr/bin/php -qC
<?php
 #
 # type: cli
 # description: Transform "type hints" into function parameter type assert() statements.
 # version: 0.1.0
 #
 # Duplicates parameter value-type hints into static lists of assertions atop
 # each function body. Accepts in-comment "type hints". Can also remove them
 # again. It recognizes scalar-ish types, arrays and resources:
 #
 #    function xyz (/*int*/ $a, /*string*/ $s, /*bool*/ $b)
 #    {
 #        assert('is_int($a) /*typehint*/');
 #        assert('is_string($s) || $s instanceof SplString /*typehint*/');
 #        assert('is_bool($b) /*typehint*/');
 #
 # Even allows a few basic expressions or comparisons within assertions:
 #
 #    function cmp (/*int,>0*/ $i, /*str,strlen($)<32*/, $s)
 #
 # Such type assertions can be turned off at runtime, be configured to generate
 # E_WARNINGs instead, or permit custom handlers. Can be selectively displaced
 # once PHP7 "type hints" become available (implicit type casts or runtime errors
 # preclude any traceable assertions of course).
 #
 # To add assertions:
 #   php-assert-hints files/*.php
 #
 # Remove them:
 #   php-assert-hints --rm files/*.php
 #


// Loop over input files
list($files, $opts) = argv();
$files or exit("Usage:\n  php-assert-hints *.php\n");
array_map(
    [new FuncDeclRewriteMap($opts), "on"],
    $files
);


/**
 * Transforms input files
 *
 */
class FuncDeclRewriteMap {

    // Parameter types to assert conditions, some aliases
    public $types = [
        "int" => "is_int($)",
        "integer" => "is_integer($)",
        "long" => "is_long($)",
        "bool" => "is_boolean($)",
        "boolean" => "is_boolean($)",
        "str" => "is_string($) || $ instanceof SplString",
        "string" => "is_string($) || $ instanceof SplString",
        "float" => "is_float($)",
        "real" => "is_real($)",
        "double" => "is_double($)",
        "numeric" => "is_numeric($)",
        "array" => "is_array($) || $ instanceof Traversable || $ instanceof ArrayObject",
        "scalar" => "is_scalar($)",
        "resource" => "is_resource($) || $ instanceof PDO",
        "callable" => "is_callable($)",
        "isset" => "isset($)",  # only makes sense for references
        "=null" => " || is_null($)",  # added for =NULL defaults
    ];
    public $only_rm = false;


    // Retain --rm cmdline flag
    public function __construct (/*array*/ $opts) {
        $this->only_rm = preg_grep("/^-+(rm?|de?l?)$/i", $opts);
    }
    

    // Update file in-place
    public function on (/*string*/ $fn) {

        $src = file_get_contents($fn);
        if ($update = $this->rewrite($src)) {
        
            // check that only lines containing assert() have been added/removed/changed
            $diff = array_diff(preg_split("/\R/", $src), preg_split("/\R/", $src));
            if (preg_grep("/^\s*assert\(/mi", $diff, PREG_GREP_INVERT)) {
                fwrite(STDERR, "Invalid rewrite (changed too much) for '$fn'\n");
            }
            
            // write back
            else {
                file_put_contents($fn, $update);
            }
        }
        
        elseif (preg_last_error()) {
            fwrite(STDERR, "Probable regex/UTF8 error with '$fn'\n");
        }
    }


    // Update source
    public function rewrite(/*string*/ $src) {
    
        // remove prior assertions
        $src = preg_replace(self::RX_ASSERT, "", $src);
        if ($this->only_rm) {
            return $src;
        }
        
        // find function bodies, and inject new assert() lists
        $src = preg_replace_callback(self::RX_FUNC, [$this, "rx_func"], $src);
        return $src;
    }
    

    // Regexps
    const RX_ASSERT = "/
       ^\h* assert\(\' [^{'}]+ \/\*\h*type\h*hint\h*\*\/ \'\);\h*\R
    /mix";
    const RX_FUNC = "/
        (?<indent> \h*)
        (?:(?:public|private|protected|static)\s+)*
        function
        \s*
        (?<name> [\w\pL]+)
        \s*
        \( (?<params> [^{}]* ) \)
        (?<rettype> \s*:\s* \w+)?
        \s*
        \{ (\h*\R)?
    /mix";
    const RX_PARAM = "/
       (?:
          (?: \/\*\s*)?
          (?<type>\w+)
          (?<expr>(?:[,\s]*[\w()$]*[<>!=]\s*[-+\d.]+[\w()]*)*)
          (?: \s*\*\/\s*|\s+)
       )?
       (\s* \&)?
       \s* (?<name>[$][\w\pL]+)
       (?<def> \s*=\s* NULL)?
    /mix";


    // Append to function declaration headers
    public function rx_func (/*array*/ $match) {
    
        // whole function declaration header
        $decl = $match[0];
        $ws = preg_replace("/\S/", " ", $match["indent"]) . "    ";
        
        // list parameters
        if (preg_match_all(self::RX_PARAM, $match["params"], $params, PREG_SET_ORDER)) {
            foreach ($params as $p) {

                // recognized type prefix?
                if ($type = strtolower($p["type"]) and isset($this->types[$type])) {

                    // copy type assertion
                    $expr = $this->types[$type];

                    // add expression checks
                    if (!empty($p["expr"])) {
                        $expr = join(" && ", array_merge(
                            ["($expr)"],
                            array_map(
                                function($add_x) {
                                    return is_int(strpos($add_x, "$"))
                                         ? "($add_x)"
                                         : "(\$ {$add_x})";
                                },
                                array_filter(preg_split("/\s*,\s*/", $p["expr"]))
                            )
                        ));
                    }

                    // add null alternative
                    if (isset($p["def"]) && stristr($p["def"], "NULL")) {
                        $expr .= $this->types["=null"];
                    }
                    
                    // substitute param name and just append to function declaration header
                    $expr = preg_replace("/[\$](?!\w)/", $p["name"], $expr);
                    $decl .= "{$ws}assert('$expr /*typehint*/');\n";
                }
            }
        }
        
        return $decl;
    }

}



// Split files from -options
function argv(/*int*/ $x=NULL) {
    $argv = array_slice($_SERVER["argv"], 1);
    $opts = preg_grep("/^-+(rm?|de?l?)/i", $argv);
    return [array_diff($argv, $opts), $opts];
}


#include <ispect.ph>