|
| 1 | +<?php |
| 2 | +/* |
| 3 | + OracleEditor.php Command Line Interface |
| 4 | + Version 1.20 |
| 5 | + by Tim Strehle <[email protected]> |
| 6 | +
|
| 7 | + https://github.com/tistre/oracleeditor |
| 8 | +
|
| 9 | + Usage: |
| 10 | +
|
| 11 | + php OracleCLI.php \ |
| 12 | + --user=me \ |
| 13 | + --password=secret \ |
| 14 | + --service='oracle.example.com:1721/DB' \ |
| 15 | + --limit=10 \ |
| 16 | + --format=xml \ |
| 17 | + --query="select * from DUAL" |
| 18 | +*/ |
| 19 | + |
| 20 | +// Don't write PHP warnings into HTML. Watch your PHP error_log file! |
| 21 | + |
| 22 | +ini_set('display_errors', 0); |
| 23 | + |
| 24 | +// Format version string |
| 25 | + |
| 26 | +$version = trim(substr('$Revision: 1.20 $', 10, -1)); |
| 27 | + |
| 28 | +// Read command line options |
| 29 | + |
| 30 | +$defaults = [ |
| 31 | + 'format' => 'xml', |
| 32 | + 'limit' => 100 |
| 33 | +]; |
| 34 | + |
| 35 | +$options = getopt('', ['user:', 'password:', 'service:', 'format:', 'query:', 'limit:']); |
| 36 | + |
| 37 | +$requiredOptions = ['user', 'password', 'service', 'query']; |
| 38 | + |
| 39 | +foreach ($requiredOptions as $option) { |
| 40 | + if (!isset($options[$option])) { |
| 41 | + die(sprintf("ERROR: --%s is required.\n", $option)); |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +$options = array_merge($defaults, $options); |
| 46 | + |
| 47 | +if (!is_numeric($options['limit'])) { |
| 48 | + die("ERROR: --limit must be a number.\n"); |
| 49 | +} |
| 50 | + |
| 51 | +$exportformats = [ |
| 52 | + 'xml' => ['XML', 'text/xml'], |
| 53 | + 'csv' => ['CSV', 'text/comma-separated-values'], |
| 54 | + 'html' => ['HTML table', 'text/html'] |
| 55 | +]; |
| 56 | + |
| 57 | +if (!isset($exportformats[$options['format']])) { |
| 58 | + die(sprintf("ERROR: --format must be one of %s.\n", implode(', ', array_keys($exportformats)))); |
| 59 | +} |
| 60 | + |
| 61 | +// Initialize database connection parameters |
| 62 | + |
| 63 | +if ($options['user'] !== substr(preg_replace('/[^a-zA-Z0-9$_-]/', '', |
| 64 | + $options['user']), 0, 30)) { |
| 65 | + die("ERROR: --user contains invalid characters.\n"); |
| 66 | +} |
| 67 | + |
| 68 | +if ($options['service'] !== substr(preg_replace('|[^a-zA-Z0-9:.() =/_-]|', '', |
| 69 | + $options['service']), 0, 2000)) { |
| 70 | + die("ERROR: --service contains invalid characters.\n"); |
| 71 | +} |
| 72 | + |
| 73 | +$charset = 'UTF-8'; |
| 74 | + |
| 75 | +// Initialize connection |
| 76 | + |
| 77 | +$conn = false; |
| 78 | + |
| 79 | +pof_connect(); |
| 80 | + |
| 81 | +// Exporting may take a while |
| 82 | + |
| 83 | +set_time_limit(0); |
| 84 | + |
| 85 | +// Initialize export settings |
| 86 | + |
| 87 | +$exportlimit = abs(intval($options['limit'])); |
| 88 | + |
| 89 | +// Loop through results |
| 90 | + |
| 91 | +$cursor = pof_opencursor($options['query']); |
| 92 | + |
| 93 | +if ($cursor) { |
| 94 | + if (ocistatementtype($cursor) !== 'SELECT') { |
| 95 | + pof_closecursor($cursor); |
| 96 | + die("ERROR: --query must be a SELECT statement.\n"); |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +$columns = []; |
| 101 | +$numcols = ocinumcols($cursor); |
| 102 | + |
| 103 | +for ($j = 1; $j <= $numcols; $j++) { |
| 104 | + if (ocicolumnname($cursor, $j) != 'ROWID_') { |
| 105 | + $columns[(ocicolumnname($cursor, $j))] = array( |
| 106 | + 'type' => ocicolumntype($cursor, $j), |
| 107 | + 'size' => ocicolumnsize($cursor, $j) |
| 108 | + ); |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +// Header |
| 113 | + |
| 114 | +if ($options['format'] == 'xml') { |
| 115 | + echo sprintf('<' . '?xml version="1.0" encoding="%s"?' . '>', $charset) . "\n"; |
| 116 | + echo "<!-- Generated by OracleEditor.php (https://github.com/tistre/oracleeditor) -->\n"; |
| 117 | + |
| 118 | + $userstr = $options['user']; |
| 119 | + $userstr .= '@' . $options['service']; |
| 120 | + |
| 121 | + echo sprintf('<rowset exported="%s" user="%s" server="%s">', date('Y-m-d\TH:i:s'), $userstr, |
| 122 | + $_SERVER['SERVER_NAME']) . "\n"; |
| 123 | + echo sprintf("\t<sql>%s</sql>\n", htmlspecialchars($options['query'])); |
| 124 | + |
| 125 | + // Column aliases: We can use column names as tag names only if |
| 126 | + // they're valid XML names - <count(MYFIELD)> won't work. |
| 127 | + |
| 128 | + $i = 0; |
| 129 | + foreach ($columns as $name => $column) { |
| 130 | + $i++; |
| 131 | + |
| 132 | + if (preg_match('/^[a-zA-Z][a-zA-Z0-9_-]*$/', $name) == 0) { |
| 133 | + $columns[$name]['alias'] = 'ALIAS' . $i; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + echo "\t<columns>\n"; |
| 138 | + foreach ($columns as $name => $column) { |
| 139 | + echo sprintf("\t\t" . '<column name="%s" type="%s" size="%s"%s/>' . "\n", |
| 140 | + htmlspecialchars($name), |
| 141 | + $column['type'], |
| 142 | + $column['size'], |
| 143 | + (isset($column['alias']) ? ' alias="' . $column['alias'] . '"' : '') |
| 144 | + ); |
| 145 | + } |
| 146 | + echo "\t</columns>\n"; |
| 147 | +} elseif ($options['format'] == 'csv') { |
| 148 | + $first = true; |
| 149 | + |
| 150 | + foreach ($columns as $name => $column) { |
| 151 | + if ($name != 'ROWID_') { |
| 152 | + if (!$first) { |
| 153 | + echo ', '; |
| 154 | + } |
| 155 | + echo sprintf('"%s"', str_replace('"', '""', $name)); |
| 156 | + $first = false; |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + echo "\n"; |
| 161 | +} elseif ($options['format'] == 'html') { ?> |
| 162 | + |
| 163 | + <html> |
| 164 | + <head> |
| 165 | + <meta http-equiv="content-type" content="text/html; charset=<?php echo $charset; ?>"> |
| 166 | + <meta name="date" content="<?php echo date('Y-m-d\TH:i:s'); ?>"> |
| 167 | + <meta name="generator" content="OracleEditor.php (https://github.com/tistre/oracleeditor)"> |
| 168 | + <title>Exported Oracle data (by OracleEditor.php)</title> |
| 169 | + </head> |
| 170 | + <body> |
| 171 | + |
| 172 | + <h1>Exported Oracle data</h1> |
| 173 | + |
| 174 | + <?php |
| 175 | + $userstr = $options['user']; |
| 176 | + $userstr .= '@' . $options['service']; |
| 177 | + ?> |
| 178 | + |
| 179 | + <p>The Oracle user <em><?php echo htmlspecialchars($userstr); ?></em> exported this data on |
| 180 | + <em><?php echo date('r'); ?></em> |
| 181 | + by running the following SQL statement in OracleCLI.php:<br/> |
| 182 | + <pre><?php echo htmlspecialchars($options['query']); ?></pre></p> |
| 183 | + |
| 184 | + <table border="1"> |
| 185 | + <tr> |
| 186 | + |
| 187 | + <?php |
| 188 | + |
| 189 | + foreach ($columns as $name => $column) { |
| 190 | + echo sprintf('<th>%s<br />(%s, %s)</th>' . "\n", |
| 191 | + htmlspecialchars($name), |
| 192 | + $column['type'], |
| 193 | + $column['size'] |
| 194 | + ); |
| 195 | + } |
| 196 | + |
| 197 | + ?> |
| 198 | + |
| 199 | + </tr> |
| 200 | + |
| 201 | + <?php |
| 202 | +} |
| 203 | + |
| 204 | +// Rows |
| 205 | + |
| 206 | +$i = 1; |
| 207 | + |
| 208 | +while (true) { |
| 209 | + if (!ocifetchinto($cursor, $row, OCI_ASSOC | OCI_RETURN_LOBS)) { |
| 210 | + break; |
| 211 | + } |
| 212 | + |
| 213 | + if ($options['format'] == 'xml') { |
| 214 | + echo sprintf("\t<row%s>\n", |
| 215 | + (isset($row['ROWID_']) ? (' id="' . htmlspecialchars($row['ROWID_']) . '"') : '')); |
| 216 | + |
| 217 | + foreach ($row as $fieldname => $value) { |
| 218 | + if ($fieldname != 'ROWID_') { |
| 219 | + echo sprintf("\t\t<%1\$s>%2\$s</%1\$s>\n", |
| 220 | + (isset($columns[$fieldname]['alias']) ? $columns[$fieldname]['alias'] : $fieldname), |
| 221 | + htmlspecialchars($value)); |
| 222 | + } |
| 223 | + } |
| 224 | + |
| 225 | + echo "\t</row>\n"; |
| 226 | + } elseif ($options['format'] == 'csv') { |
| 227 | + $first = true; |
| 228 | + |
| 229 | + foreach ($columns as $fieldname => $column) { |
| 230 | + if ($fieldname != 'ROWID_') { |
| 231 | + if (!$first) { |
| 232 | + echo ', '; |
| 233 | + } |
| 234 | + if (isset($row[$fieldname])) { |
| 235 | + echo sprintf('"%s"', str_replace('"', '""', $row[$fieldname])); |
| 236 | + } else { |
| 237 | + echo '""'; |
| 238 | + } |
| 239 | + $first = false; |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + echo "\n"; |
| 244 | + } elseif ($options['format'] == 'html') { |
| 245 | + echo "<tr>\n"; |
| 246 | + |
| 247 | + foreach ($columns as $fieldname => $column) { |
| 248 | + if ($fieldname != 'ROWID_') { |
| 249 | + echo "\t<td>"; |
| 250 | + if (isset($row[$fieldname])) { |
| 251 | + echo htmlspecialchars($row[$fieldname]); |
| 252 | + } |
| 253 | + echo "</td>\n"; |
| 254 | + } |
| 255 | + } |
| 256 | + |
| 257 | + echo "</tr>\n"; |
| 258 | + } |
| 259 | + |
| 260 | + if (($exportlimit > 0) && ($exportlimit <= ++$i)) { |
| 261 | + break; |
| 262 | + } |
| 263 | +} |
| 264 | + |
| 265 | +// Footer |
| 266 | + |
| 267 | +if ($options['format'] == 'xml') { |
| 268 | + echo "</rowset>\n"; |
| 269 | +} elseif ($options['format'] == 'html') { ?> |
| 270 | + |
| 271 | + </table> |
| 272 | + <p>HTML generated by <a |
| 273 | + href="https://github.com/tistre/oracleeditor">OracleEditor.php</a> <?php echo $version; ?> |
| 274 | + © 2017 by <a href="https://www.strehle.de/tim/">Tim Strehle</a> <<a |
| 275 | + |
| 276 | + </body> |
| 277 | + </html> |
| 278 | + |
| 279 | + <?php |
| 280 | +} |
| 281 | + |
| 282 | +pof_closecursor($cursor); |
| 283 | + |
| 284 | + |
| 285 | +function pof_connect() |
| 286 | +{ |
| 287 | + global $conn, $options; |
| 288 | + |
| 289 | + $conn = oci_connect |
| 290 | + ( |
| 291 | + $options['user'], |
| 292 | + $options['password'], |
| 293 | + $options['service'], |
| 294 | + 'AL32UTF8' |
| 295 | + ); |
| 296 | + |
| 297 | + $err = ocierror(); |
| 298 | + |
| 299 | + if (is_array($err)) { |
| 300 | + die(sprintf("ERROR: Logon failed: %s\n", $err['message'])); |
| 301 | + } |
| 302 | +} |
| 303 | + |
| 304 | + |
| 305 | +function pof_disconnect() |
| 306 | +{ |
| 307 | + global $conn; |
| 308 | + |
| 309 | + if ($conn) { |
| 310 | + ocilogoff($conn); |
| 311 | + } |
| 312 | +} |
| 313 | + |
| 314 | + |
| 315 | +function pof_opencursor($sql, $bind = false) |
| 316 | +{ |
| 317 | + global $conn, $options; |
| 318 | + |
| 319 | + $cursor = ociparse($conn, $sql); |
| 320 | + |
| 321 | + if (!$cursor) { |
| 322 | + $err = ocierror($conn); |
| 323 | + if (is_array($err)) { |
| 324 | + die(sprintf("ERROR: Parse failed: %s\n", $err['message'])); |
| 325 | + } |
| 326 | + } else { // This might improve performance? |
| 327 | + ocisetprefetch($cursor, $options['limit']); |
| 328 | + |
| 329 | + if (is_array($bind)) { |
| 330 | + foreach ($bind as $fieldname => $value) { |
| 331 | + ocibindbyname($cursor, ':' . $fieldname, $bind[$fieldname], -1); |
| 332 | + } |
| 333 | + } |
| 334 | + |
| 335 | + $ok = ociexecute($cursor); |
| 336 | + |
| 337 | + if (!$ok) { |
| 338 | + $err = ocierror($cursor); |
| 339 | + |
| 340 | + if (is_array($err)) { |
| 341 | + die(sprintf("ERROR: Execute failed: %s\n", $err['message'])); |
| 342 | + } |
| 343 | + |
| 344 | + pof_closecursor($cursor); |
| 345 | + |
| 346 | + $cursor = false; |
| 347 | + } |
| 348 | + } |
| 349 | + |
| 350 | + return $cursor; |
| 351 | +} |
| 352 | + |
| 353 | + |
| 354 | +function pof_closecursor($cursor) |
| 355 | +{ |
| 356 | + if ($cursor) { |
| 357 | + ocifreestatement($cursor); |
| 358 | + } |
| 359 | +} |
0 commit comments