1 | <?php |
||
2 | |||
3 | /* Copyright (C) 2015 Jean-François Ferry <[email protected]> |
||
4 | * Copyright (C) 2016 Laurent Destailleur <[email protected]> |
||
5 | * Copyright (C) 2017 Regis Houssin <[email protected]> |
||
6 | * Copyright (C) 2021 Alexis LAURIER <[email protected]> |
||
7 | * Copyright (C) 2024 MDW <[email protected]> |
||
8 | * Copyright (C) 2024 Rafael San José <[email protected]> |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or modify |
||
11 | * it under the terms of the GNU General Public License as published by |
||
12 | * the Free Software Foundation; either version 3 of the License, or |
||
13 | * (at your option) any later version. |
||
14 | * |
||
15 | * This program is distributed in the hope that it will be useful, |
||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | * GNU General Public License for more details. |
||
19 | * |
||
20 | * You should have received a copy of the GNU General Public License |
||
21 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||
22 | */ |
||
23 | |||
24 | use Dolibarr\Code\Api\Classes\DolibarrApiAccess; |
||
25 | use Dolibarr\Core\Base\DolibarrApi; |
||
26 | use Luracast\Restler\Format\UploadFormat; |
||
27 | |||
28 | $api_route = $_GET['api_route'] ?? ''; |
||
29 | unset($_GET['api_route']); |
||
30 | $_SERVER['SCRIPT_NAME'] = '/api/index.php'; |
||
31 | |||
32 | if (!defined('NOCSRFCHECK')) { |
||
33 | define('NOCSRFCHECK', '1'); // Do not check anti CSRF attack test |
||
34 | } |
||
35 | if (!defined('NOTOKENRENEWAL')) { |
||
36 | define('NOTOKENRENEWAL', '1'); // Do not check anti POST attack test |
||
37 | } |
||
38 | if (!defined('NOREQUIREMENU')) { |
||
39 | define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu |
||
40 | } |
||
41 | if (!defined('NOREQUIREHTML')) { |
||
42 | define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php |
||
43 | } |
||
44 | if (!defined('NOREQUIREAJAX')) { |
||
45 | define('NOREQUIREAJAX', '1'); // Do not load ajax.lib.php library |
||
46 | } |
||
47 | if (!defined("NOLOGIN")) { |
||
48 | define("NOLOGIN", '1'); // If this page is public (can be called outside logged session) |
||
49 | } |
||
50 | if (!defined("NOSESSION")) { |
||
51 | define("NOSESSION", '1'); |
||
52 | } |
||
53 | if (!defined("NODEFAULTVALUES")) { |
||
54 | define("NODEFAULTVALUES", '1'); |
||
55 | } |
||
56 | |||
57 | // Force entity if a value is provided into HTTP header. Otherwise, will use the entity of user of token used. |
||
58 | if (!empty($_SERVER['HTTP_DOLAPIENTITY'])) { |
||
59 | define("DOLENTITY", (int)$_SERVER['HTTP_DOLAPIENTITY']); |
||
60 | } |
||
61 | |||
62 | // Response for preflight requests (used by browser when into a CORS context) |
||
63 | if (!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS' && !empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) { |
||
64 | header('Access-Control-Allow-Origin: *'); |
||
65 | header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); |
||
66 | header('Access-Control-Allow-Headers: Content-Type, Authorization, api_key, DOLAPIKEY'); |
||
67 | http_response_code(204); |
||
68 | exit; |
||
69 | } |
||
70 | |||
71 | // When we request url to get the json file, we accept Cross site so we can include the descriptor into an external tool. |
||
72 | if (str_contains($_SERVER["PHP_SELF"], '/explorer/swagger.json')) { |
||
73 | header('Access-Control-Allow-Origin: *'); |
||
74 | header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); |
||
75 | header('Access-Control-Allow-Headers: Content-Type, Authorization, api_key, DOLAPIKEY'); |
||
76 | } |
||
77 | |||
78 | // When we request url to get an API, we accept Cross site so we can make js API call inside another website |
||
79 | if (str_contains($_SERVER["PHP_SELF"], '/api/index.php')) { |
||
80 | header('Access-Control-Allow-Origin: *'); |
||
81 | header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); |
||
82 | header('Access-Control-Allow-Headers: Content-Type, Authorization, api_key, DOLAPIKEY'); |
||
83 | } |
||
84 | header('X-Frame-Options: SAMEORIGIN'); |
||
85 | |||
86 | $res = 0; |
||
87 | $res = include constant('DOL_DOCUMENT_ROOT') . "/main.inc.php"; |
||
88 | if (!$res) { |
||
89 | die("Include of main fails"); |
||
90 | } |
||
91 | |||
92 | require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php'; |
||
93 | |||
94 | $url = $_SERVER['PHP_SELF']; |
||
95 | if (str_ends_with($_SERVER['PHP_SELF'], 'api/index.php')) { |
||
96 | $additionalPath = !empty($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ($_SERVER['ORIG_PATH_INFO'] ?? ''); |
||
97 | $url = $_SERVER['PHP_SELF'] . $additionalPath; |
||
98 | } |
||
99 | |||
100 | // Fix for some NGINX setups (this should not be required even with NGINX, however setup of NGINX are often mysterious and this may help is such cases) |
||
101 | if (getDolGlobalString('MAIN_NGINX_FIX')) { |
||
102 | $url = (isset($_SERVER['SCRIPT_URI']) && $_SERVER["SCRIPT_URI"] !== null) ? $_SERVER["SCRIPT_URI"] : $_SERVER['PHP_SELF']; |
||
103 | } |
||
104 | |||
105 | // Enable and test if module Api is enabled |
||
106 | if (!isModEnabled('api')) { |
||
107 | $langs->load("admin"); |
||
108 | dol_syslog("Call of Dolibarr API interfaces with module API REST are disabled"); |
||
109 | print $langs->trans("WarningModuleNotActive", 'Api') . '.<br><br>'; |
||
110 | print $langs->trans("ToActivateModule"); |
||
111 | //session_destroy(); |
||
112 | exit(0); |
||
113 | } |
||
114 | |||
115 | // Test if explorer is not disabled |
||
116 | if (str_contains($url, 'api/index.php/explorer') && getDolGlobalString('API_EXPLORER_DISABLED')) { |
||
117 | $langs->load("admin"); |
||
118 | dol_syslog("Call Dolibarr API interfaces with module API REST disabled"); |
||
119 | print $langs->trans("WarningAPIExplorerDisabled") . '.<br><br>'; |
||
120 | //session_destroy(); |
||
121 | exit(0); |
||
122 | } |
||
123 | |||
124 | // This 2 lines are useful only if we want to exclude some Urls from the explorer |
||
125 | //use Luracast\Restler\Explorer; |
||
126 | //Explorer::$excludedPaths = array('/categories'); |
||
127 | |||
128 | |||
129 | // Analyze URLs |
||
130 | // index.php/explorer do a redirect to index.php/explorer/ |
||
131 | // index.php/explorer/ called by swagger to build explorer page index.php/explorer/index.html |
||
132 | // index.php/explorer/.../....png|.css|.js called by swagger for resources to build explorer page |
||
133 | // index.php/explorer/resources.json called by swagger to get list of all services |
||
134 | // index.php/explorer/resources.json/xxx called by swagger to get detail of services xxx |
||
135 | // index.php/xxx called by any REST client to run API |
||
136 | |||
137 | $reg = array(); |
||
138 | preg_match('/index\.php\/([^\/]+)(.*)$/', $url, $reg); |
||
139 | // .../index.php/categories?sortfield=t.rowid&sortorder=ASC |
||
140 | |||
141 | $hookmanager->initHooks(array('api')); |
||
142 | |||
143 | // When in production mode, a file api/temp/routes.php is created with the API available of current call. |
||
144 | // But, if we set $refreshcache to false, so it may have only one API in the routes.php file if we make a call for one API without |
||
145 | // using the explorer. And when we make another call for another API, the API is not into the api/temp/routes.php and a 404 is returned. |
||
146 | // So we force refresh to each call. |
||
147 | $refreshcache = (getDolGlobalString('API_PRODUCTION_DO_NOT_ALWAYS_REFRESH_CACHE') ? false : true); |
||
148 | if (!empty($reg[1]) && $reg[1] == 'explorer' && ($reg[2] == '/swagger.json' || $reg[2] == '/swagger.json/root' || $reg[2] == '/resources.json' || $reg[2] == '/resources.json/root')) { |
||
149 | $refreshcache = true; |
||
150 | if (!is_dir($conf->api->dir_temp)) { |
||
151 | mkdir($conf->api->dir_temp); |
||
152 | } |
||
153 | if (!is_writable($conf->api->dir_temp)) { |
||
154 | print 'Erreur temp dir api/temp (' . $conf->api->dir_temp . ') not writable'; |
||
155 | header('HTTP/1.1 500 temp dir api/temp not writable'); |
||
156 | exit(0); |
||
157 | } |
||
158 | } |
||
159 | |||
160 | $api = new DolibarrApi($db, '', $refreshcache); |
||
161 | // var_dump($api->r->apiVersionMap); |
||
162 | |||
163 | // If MAIN_API_DEBUG is set to 1, we save logs into file "dolibarr_api.log" |
||
164 | if (getDolGlobalString('MAIN_API_DEBUG')) { |
||
165 | $r = $api->r; |
||
166 | $r->onCall(function () use ($r) { |
||
167 | // Don't log Luracast Restler Explorer resources calls |
||
168 | //if (!preg_match('/^explorer/', $r->url)) { |
||
169 | // 'method' => $api->r->requestMethod, |
||
170 | // 'url' => $api->r->url, |
||
171 | // 'route' => $api->r->apiMethodInfo->className.'::'.$api->r->apiMethodInfo->methodName, |
||
172 | // 'version' => $api->r->getRequestedApiVersion(), |
||
173 | // 'data' => $api->r->getRequestData(), |
||
174 | //dol_syslog("Debug API input ".var_export($r, true), LOG_DEBUG, 0, '_api'); |
||
175 | dol_syslog("Debug API url " . var_export($r->url, true), LOG_DEBUG, 0, '_api'); |
||
176 | dol_syslog("Debug API input " . var_export($r->getRequestData(), true), LOG_DEBUG, 0, '_api'); |
||
177 | //} |
||
178 | }); |
||
179 | } |
||
180 | |||
181 | // Enable the Restler API Explorer. |
||
182 | // See https://github.com/Luracast/Restler-API-Explorer for more info. |
||
183 | $api->r->addAPIClass('Luracast\\Restler\\Explorer'); |
||
184 | $api->r->addAPIClass('Luracast\\Restler\\Explorer\\v1\\Explorer'); |
||
185 | $api->r->addAPIClass('Luracast\\Restler\\Explorer\\v2\\Explorer'); |
||
186 | |||
187 | $api->r->setSupportedFormats('JsonFormat', 'XmlFormat', 'UploadFormat'); // 'YamlFormat' |
||
188 | $api->r->addAuthenticationClass('Dolibarr\\Code\\Api\\Classes\\DolibarrApiAccess', ''); |
||
189 | |||
190 | // Define accepted mime types |
||
191 | UploadFormat::$allowedMimeTypes = array('image/jpeg', 'image/png', 'text/plain', 'application/octet-stream'); |
||
192 | |||
193 | // Restrict API to some IPs |
||
194 | if (getDolGlobalString('API_RESTRICT_ON_IP')) { |
||
195 | $allowedip = explode(' ', getDolGlobalString('API_RESTRICT_ON_IP')); |
||
196 | $ipremote = getUserRemoteIP(); |
||
197 | if (!in_array($ipremote, $allowedip)) { |
||
198 | dol_syslog('Remote ip is ' . $ipremote . ', not into list ' . getDolGlobalString('API_RESTRICT_ON_IP')); |
||
199 | print 'APIs are not allowed from the IP ' . $ipremote; |
||
200 | header('HTTP/1.1 503 API not allowed from your IP ' . $ipremote); |
||
201 | //session_destroy(); |
||
202 | exit(0); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | // Call Explorer file for all APIs definitions (this part is slow) |
||
207 | if (!empty($reg[1]) && $reg[1] == 'explorer' && ($reg[2] == '/swagger.json' || $reg[2] == '/swagger.json/' || $reg[2] == '/swagger.json/root' || $reg[2] == '/resources.json/' || $reg[2] == '/resources.json' || $reg[2] == '/resources.json/root')) { |
||
208 | // Scan all API files to load them |
||
209 | $listofapis = DolibarrApi::getModules(); |
||
210 | foreach ($listofapis as $apiname => $classname) { |
||
211 | new $classname(); |
||
212 | $api->r->addAPIClass($classname); |
||
213 | } |
||
214 | } |
||
215 | |||
216 | |||
217 | // Call one APIs or one definition of an API |
||
218 | $regbis = array(); |
||
219 | if (!empty($reg[1]) && ($reg[1] != 'explorer' || ($reg[2] != '/swagger.json' && $reg[2] != '/resources.json' && preg_match('/^\/(swagger|resources)\.json\/(.+)$/', $reg[2], $regbis) && $regbis[2] != 'root'))) { |
||
220 | $moduleobject = $reg[1]; |
||
221 | if ($moduleobject == 'explorer') { // If we call page to explore details of a service |
||
222 | $moduleobject = $regbis[2]; |
||
223 | } |
||
224 | |||
225 | $moduleobject = strtolower($moduleobject); |
||
226 | $moduledirforclass = getModuleDirForApiClass($moduleobject); |
||
227 | |||
228 | // Load a dedicated API file |
||
229 | dol_syslog("Load a dedicated API file moduleobject=" . $moduleobject . " moduledirforclass=" . $moduledirforclass); |
||
230 | |||
231 | $tmpmodule = $moduleobject; |
||
232 | if ($tmpmodule != 'api') { |
||
233 | $tmpmodule = preg_replace('/api$/i', '', $tmpmodule); |
||
234 | } |
||
235 | $classfile = str_replace('_', '', $tmpmodule); |
||
236 | |||
237 | // Special cases that does not match name rules conventions |
||
238 | if ($moduleobject == 'supplierproposals') { |
||
239 | $classfile = 'supplier_proposals'; |
||
240 | } |
||
241 | if ($moduleobject == 'supplierorders') { |
||
242 | $classfile = 'supplier_orders'; |
||
243 | } |
||
244 | if ($moduleobject == 'supplierinvoices') { |
||
245 | $classfile = 'supplier_invoices'; |
||
246 | } |
||
247 | if ($moduleobject == 'ficheinter') { |
||
248 | $classfile = 'interventions'; |
||
249 | } |
||
250 | if ($moduleobject == 'interventions') { |
||
251 | $classfile = 'interventions'; |
||
252 | } |
||
253 | |||
254 | $filename = '/' . $moduledirforclass . '/class/api_' . $classfile . '.class.php'; |
||
255 | $dir_part_file = dol_buildpath($filename, 0, 2); |
||
256 | |||
257 | $classname = ucwords($moduleobject); |
||
258 | $modulename = ucwords($moduledirforclass); |
||
259 | |||
260 | // Test rules on endpoints. For example: |
||
261 | // $conf->global->API_ENDPOINT_RULES = 'endpoint1:1,endpoint2:1,...' |
||
262 | if (getDolGlobalString('API_ENDPOINT_RULES')) { |
||
263 | $listofendpoints = explode(',', getDolGlobalString('API_ENDPOINT_RULES')); |
||
264 | $endpointisallowed = false; |
||
265 | |||
266 | foreach ($listofendpoints as $endpointrule) { |
||
267 | $tmparray = explode(':', $endpointrule); |
||
268 | if (($classfile == $tmparray[0] || $classfile . 'api' == $tmparray[0]) && $tmparray[1] == 1) { |
||
269 | $endpointisallowed = true; |
||
270 | break; |
||
271 | } |
||
272 | } |
||
273 | |||
274 | if (!$endpointisallowed) { |
||
275 | dol_syslog('The API with endpoint /' . $classfile . ' is forbidden by config API_ENDPOINT_RULES', LOG_WARNING); |
||
276 | print 'The API with endpoint /' . $classfile . ' is forbidden by config API_ENDPOINT_RULES'; |
||
277 | header('HTTP/1.1 501 API is forbidden by API_ENDPOINT_RULES'); |
||
278 | //session_destroy(); |
||
279 | exit(0); |
||
280 | } |
||
281 | } |
||
282 | |||
283 | dol_syslog('Search api file /' . $moduledirforclass . '/class/api_' . $classfile . '.class.php => dir_part_file=' . $dir_part_file . ', classname=' . $classname); |
||
284 | |||
285 | $namespace = DolibarrApi::getModuleNamespace($modulename, $classname); |
||
286 | $class = new $namespace(); |
||
287 | if (!isset($class)) { |
||
288 | dol_syslog('Failed to make include_once ' . $dir_part_file, LOG_WARNING); |
||
289 | print 'API not found (failed to include API file)'; |
||
290 | header('HTTP/1.1 501 API not found (failed to include API file)'); |
||
291 | //session_destroy(); |
||
292 | exit(0); |
||
293 | } |
||
294 | |||
295 | $api->r->addAPIClass($namespace); |
||
296 | } |
||
297 | |||
298 | // We do not want that restler outputs data if we use native compression (default behaviour) but we want to have it returned into a string. |
||
299 | // If API_DISABLE_COMPRESSION is set, returnResponse is false => It use default handling so output result directly. |
||
300 | $usecompression = (!getDolGlobalString('API_DISABLE_COMPRESSION') && !empty($_SERVER['HTTP_ACCEPT_ENCODING'])); |
||
301 | $foundonealgorithm = 0; |
||
302 | if ($usecompression) { |
||
303 | if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'br') !== false && function_exists('brotli_compress')) { |
||
304 | $foundonealgorithm++; |
||
305 | } |
||
306 | if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'bz') !== false && function_exists('bzcompress')) { |
||
307 | $foundonealgorithm++; |
||
308 | } |
||
309 | if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && function_exists('gzencode')) { |
||
310 | $foundonealgorithm++; |
||
311 | } |
||
312 | if (!$foundonealgorithm) { |
||
313 | $usecompression = false; |
||
314 | } |
||
315 | } |
||
316 | |||
317 | //dol_syslog('We found some compression algorithm: '.$foundonealgorithm.' -> usecompression='.$usecompression, LOG_DEBUG); |
||
318 | |||
319 | Luracast\Restler\Defaults::$returnResponse = $usecompression; |
||
320 | |||
321 | // Call API (we suppose we found it). |
||
322 | // The handle will use the file api/temp/routes.php to get data to run the API. If the file exists and the entry for API is not found, it will return 404. |
||
323 | $responsedata = $api->r->handle(); |
||
0 ignored issues
–
show
|
|||
324 | |||
325 | if (Luracast\Restler\Defaults::$returnResponse) { |
||
326 | // We try to compress the data received data |
||
327 | if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'br') !== false && function_exists('brotli_compress') && defined('BROTLI_TEXT')) { |
||
328 | header('Content-Encoding: br'); |
||
329 | $result = brotli_compress($responsedata, 11, constant('BROTLI_TEXT')); |
||
330 | } elseif (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'bz') !== false && function_exists('bzcompress')) { |
||
331 | header('Content-Encoding: bz'); |
||
332 | $result = bzcompress($responsedata, 9); |
||
333 | } elseif (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && function_exists('gzencode')) { |
||
334 | header('Content-Encoding: gzip'); |
||
335 | $result = gzencode($responsedata, 9); |
||
336 | } else { |
||
337 | header('Content-Encoding: text/html'); |
||
338 | print "No compression method found. Try to disable compression by adding API_DISABLE_COMPRESSION=1"; |
||
339 | exit(0); |
||
340 | } |
||
341 | |||
342 | // Restler did not output data yet, we return it now |
||
343 | echo $result; |
||
344 | } |
||
345 | |||
346 | if (getDolGlobalInt("API_ENABLE_COUNT_CALLS") && $api->r->responseCode == 200) { |
||
347 | $error = 0; |
||
348 | $db->begin(); |
||
349 | $userid = DolibarrApiAccess::$user->id; |
||
350 | |||
351 | $sql = "SELECT up.value"; |
||
352 | $sql .= " FROM " . MAIN_DB_PREFIX . "user_param as up"; |
||
353 | $sql .= " WHERE up.param = 'API_COUNT_CALL'"; |
||
354 | $sql .= " AND up.fk_user = " . ((int)$userid); |
||
355 | $sql .= " AND up.entity = " . ((int)$conf->entity); |
||
356 | |||
357 | $result = $db->query($sql); |
||
358 | if ($result) { |
||
359 | $updateapi = false; |
||
360 | $nbrows = $db->num_rows($result); |
||
361 | if ($nbrows == 0) { |
||
362 | $sql2 = "INSERT INTO " . MAIN_DB_PREFIX . "user_param"; |
||
363 | $sql2 .= " (fk_user, entity, param, value)"; |
||
364 | $sql2 .= " VALUES (" . ((int)$userid) . ", " . ((int)$conf->entity) . ", 'API_COUNT_CALL', 1)"; |
||
365 | } else { |
||
366 | $updateapi = true; |
||
367 | $sql2 = "UPDATE " . MAIN_DB_PREFIX . "user_param as up"; |
||
368 | $sql2 .= " SET up.value = up.value + 1"; |
||
369 | $sql2 .= " WHERE up.param = 'API_COUNT_CALL'"; |
||
370 | $sql2 .= " AND up.fk_user = " . ((int)$userid); |
||
371 | $sql2 .= " AND up.entity = " . ((int)$conf->entity); |
||
372 | } |
||
373 | |||
374 | $result2 = $db->query($sql2); |
||
375 | if (!$result2) { |
||
376 | $modeapicall = $updateapi ? 'updating' : 'inserting'; |
||
377 | dol_syslog('Error while ' . $modeapicall . ' API_COUNT_CALL for user ' . $userid, LOG_ERR); |
||
378 | $error++; |
||
379 | } |
||
380 | } else { |
||
381 | dol_syslog('Error on select API_COUNT_CALL for user ' . $userid, LOG_ERR); |
||
382 | $error++; |
||
383 | } |
||
384 | |||
385 | if ($error) { |
||
386 | $db->rollback(); |
||
387 | } else { |
||
388 | $db->commit(); |
||
389 | } |
||
390 | } |
||
391 | |||
392 | // Call API termination method |
||
393 | $apiMethodInfo = &$api->r->apiMethodInfo; |
||
394 | $terminateCall = '_terminate_' . $apiMethodInfo->methodName . '_' . $api->r->responseFormat->getExtension(); |
||
395 | if (method_exists($apiMethodInfo->className, $terminateCall)) { |
||
396 | // Now flush output buffers so that response data is sent to the client even if we still have action to do in a termination method. |
||
397 | ob_end_flush(); |
||
398 | |||
399 | // If you're using PHP-FPM, this function will allow you to send the response and then continue processing |
||
400 | if (function_exists('fastcgi_finish_request')) { |
||
401 | fastcgi_finish_request(); |
||
402 | } |
||
403 | |||
404 | // Call a termination method. Warning: This method can do I/O, sync but must not make output. |
||
405 | call_user_func(array(Luracast\Restler\Scope::get($apiMethodInfo->className), $terminateCall), $responsedata); |
||
406 | } |
||
407 | |||
408 | //session_destroy(); |
||
409 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.