sokil /
php-mongo
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file is part of the PHPMongo package. |
||
| 5 | * |
||
| 6 | * (c) Dmytro Sokil <[email protected]> |
||
| 7 | * |
||
| 8 | * For the full copyright and license information, please view the LICENSE |
||
| 9 | * file that was distributed with this source code. |
||
| 10 | */ |
||
| 11 | |||
| 12 | namespace Sokil\Mongo; |
||
| 13 | |||
| 14 | use Sokil\Mongo\Collection\Definition; |
||
| 15 | |||
| 16 | class Database |
||
| 17 | { |
||
| 18 | /** |
||
| 19 | * |
||
| 20 | * @var \Sokil\Mongo\Client |
||
| 21 | */ |
||
| 22 | private $client; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * @var \MongoDB |
||
| 26 | */ |
||
| 27 | private $database; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * @var string |
||
| 31 | */ |
||
| 32 | private $databaseName; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * @var array map collection name to class |
||
| 36 | */ |
||
| 37 | private $mapping = array(); |
||
| 38 | |||
| 39 | /** |
||
| 40 | * @var array map regexp pattern of collection name to class |
||
| 41 | */ |
||
| 42 | private $regexpMapping = array(); |
||
| 43 | |||
| 44 | /** |
||
| 45 | * @var array pool of initialised collections |
||
| 46 | */ |
||
| 47 | private $collectionPool = array(); |
||
| 48 | |||
| 49 | /** |
||
| 50 | * |
||
| 51 | * @var bool is collection pool enabled |
||
| 52 | */ |
||
| 53 | private $collectionPoolEnabled = true; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * @param Client $client |
||
| 57 | * @param \MongoDB|string $database |
||
| 58 | */ |
||
| 59 | public function __construct(Client $client, $database) |
||
| 60 | { |
||
| 61 | $this->client = $client; |
||
| 62 | |||
| 63 | if ($database instanceof \MongoDB) { |
||
| 64 | $this->database = $database; |
||
| 65 | $this->databaseName = $database->__toString(); |
||
| 66 | } else { |
||
| 67 | $this->databaseName = $database; |
||
| 68 | } |
||
| 69 | } |
||
| 70 | |||
| 71 | /** |
||
| 72 | * |
||
| 73 | * @param string $username |
||
| 74 | * @param string $password |
||
| 75 | */ |
||
| 76 | public function authenticate($username, $password) |
||
| 77 | { |
||
| 78 | $this->getMongoDB()->authenticate($username, $password); |
||
| 79 | } |
||
| 80 | |||
| 81 | public function logout() |
||
| 82 | { |
||
| 83 | $this->executeCommand(array( |
||
| 84 | 'logout' => 1, |
||
| 85 | )); |
||
| 86 | } |
||
| 87 | |||
| 88 | public function __get($name) |
||
| 89 | { |
||
| 90 | return $this->getCollection($name); |
||
| 91 | } |
||
| 92 | |||
| 93 | /** |
||
| 94 | * @return string get name of database |
||
| 95 | */ |
||
| 96 | public function getName() |
||
| 97 | { |
||
| 98 | return $this->databaseName; |
||
| 99 | } |
||
| 100 | |||
| 101 | /** |
||
| 102 | * |
||
| 103 | * @return \MongoDB |
||
| 104 | */ |
||
| 105 | public function getMongoDB() |
||
| 106 | { |
||
| 107 | if (empty($this->database)) { |
||
| 108 | $this->database = $this |
||
| 109 | ->client |
||
| 110 | ->getMongoClient() |
||
| 111 | ->selectDB($this->databaseName); |
||
| 112 | } |
||
| 113 | |||
| 114 | return $this->database; |
||
| 115 | } |
||
| 116 | |||
| 117 | /** |
||
| 118 | * |
||
| 119 | * @return \Sokil\Mongo\Client |
||
| 120 | */ |
||
| 121 | public function getClient() |
||
| 122 | { |
||
| 123 | return $this->client; |
||
| 124 | } |
||
| 125 | |||
| 126 | public function disableCollectionPool() |
||
| 127 | { |
||
| 128 | $this->collectionPoolEnabled = false; |
||
| 129 | return $this; |
||
| 130 | } |
||
| 131 | |||
| 132 | public function enableCollectionPool() |
||
| 133 | { |
||
| 134 | $this->collectionPoolEnabled = true; |
||
| 135 | return $this; |
||
| 136 | } |
||
| 137 | |||
| 138 | public function isCollectionPoolEnabled() |
||
| 139 | { |
||
| 140 | return $this->collectionPoolEnabled; |
||
| 141 | } |
||
| 142 | |||
| 143 | public function clearCollectionPool() |
||
| 144 | { |
||
| 145 | $this->collectionPool = array(); |
||
| 146 | return $this; |
||
| 147 | } |
||
| 148 | |||
| 149 | public function isCollectionPoolEmpty() |
||
| 150 | { |
||
| 151 | return !$this->collectionPool; |
||
| 152 | } |
||
| 153 | |||
| 154 | /** |
||
| 155 | * Reset specified mapping |
||
| 156 | * |
||
| 157 | * @return \Sokil\Mongo\Client |
||
| 158 | */ |
||
| 159 | public function resetMapping() |
||
| 160 | { |
||
| 161 | $this->mapping = array(); |
||
| 162 | |||
| 163 | return $this; |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * Map collection name to class |
||
| 168 | * |
||
| 169 | * @param string|array $name collection name or array like |
||
| 170 | * [collectionName => collectionClass, ...] |
||
| 171 | * @param string|array|Definition|null $classDefinition if $name is string, then full class name or array |
||
| 172 | * with parameters, else omitted |
||
| 173 | * @return \Sokil\Mongo\Client |
||
| 174 | */ |
||
| 175 | public function map($name, $classDefinition = null) |
||
| 176 | { |
||
| 177 | // map collection to class |
||
| 178 | if ($classDefinition) { |
||
| 179 | return $this->defineCollection($name, $classDefinition); |
||
| 180 | } |
||
| 181 | |||
| 182 | // map collections to classes |
||
| 183 | if (is_array($name)) { |
||
| 184 | foreach ($name as $collectionName => $classDefinition) { |
||
| 185 | $this->defineCollection($collectionName, $classDefinition); |
||
| 186 | } |
||
| 187 | return $this; |
||
| 188 | } |
||
| 189 | |||
| 190 | // define class prefix |
||
| 191 | // deprecated: use class definition |
||
| 192 | $this->defineCollection('*', array( |
||
| 193 | 'class' => $name, |
||
| 194 | )); |
||
| 195 | |||
| 196 | return $this; |
||
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * Define collection through array or Definition instance |
||
| 201 | * |
||
| 202 | * @param string $name collection name |
||
| 203 | * @param Definition|array|string $definition collection definition |
||
| 204 | * @return Database |
||
| 205 | */ |
||
| 206 | private function defineCollection($name, $definition) |
||
| 207 | { |
||
| 208 | // prepare definition object |
||
| 209 | if (false === ($definition instanceof Definition)) { |
||
| 210 | if (is_string($definition)) { |
||
| 211 | $definition = new Definition(array('class' => $definition)); |
||
| 212 | } elseif (is_array($definition)) { |
||
| 213 | $definition = new Definition($definition); |
||
| 214 | } else { |
||
| 215 | throw new Exception(sprintf('Wrong definition passed for collection %s', $name)); |
||
| 216 | } |
||
| 217 | } |
||
| 218 | |||
| 219 | // set definition |
||
| 220 | if ('/' !== substr($name, 0, 1)) { |
||
| 221 | $this->mapping[$name] = $definition; |
||
| 222 | } else { |
||
| 223 | $this->regexpMapping[$name] = $definition; |
||
| 224 | } |
||
| 225 | |||
| 226 | return $this; |
||
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Get class name mapped to collection |
||
| 231 | * |
||
| 232 | * @param string $name name of collection |
||
| 233 | * @param array $defaultDefinition definition used when no definition found for defined class |
||
| 234 | * @throws Exception |
||
| 235 | * @return string|array name of class or array of class definition |
||
| 236 | */ |
||
| 237 | private function getCollectionDefinition($name, array $defaultDefinition = null) |
||
| 238 | { |
||
| 239 | if (isset($this->mapping[$name])) { |
||
| 240 | $classDefinition = $this->mapping[$name]; |
||
| 241 | } elseif ($this->regexpMapping) { |
||
|
0 ignored issues
–
show
|
|||
| 242 | foreach ($this->regexpMapping as $collectionNamePattern => $regexpMappingClassDefinition) { |
||
| 243 | if (preg_match($collectionNamePattern, $name, $matches)) { |
||
| 244 | $classDefinition = clone $regexpMappingClassDefinition; |
||
| 245 | $classDefinition->setOption('regexp', $matches); |
||
| 246 | break; |
||
| 247 | } |
||
| 248 | } |
||
| 249 | } |
||
| 250 | |||
| 251 | // mapping not configured - use default |
||
| 252 | if (!isset($classDefinition)) { |
||
| 253 | if (!empty($this->mapping['*'])) { |
||
| 254 | $classDefinition = clone $this->mapping['*']; |
||
| 255 | $collectionClass = $classDefinition->getClass() |
||
| 256 | . '\\' |
||
| 257 | . implode('\\', array_map('ucfirst', explode('.', $name))); |
||
| 258 | $classDefinition->setClass($collectionClass); |
||
| 259 | } else { |
||
| 260 | $classDefinition = new Definition(); |
||
| 261 | if ($defaultDefinition) { |
||
| 262 | $classDefinition->merge($defaultDefinition); |
||
| 263 | } |
||
| 264 | } |
||
| 265 | } |
||
| 266 | |||
| 267 | // check if class exists |
||
| 268 | if (!class_exists($classDefinition->getClass())) { |
||
| 269 | throw new Exception( |
||
| 270 | 'Class ' . $classDefinition->getClass() . ' not found while map collection name to class' |
||
| 271 | ); |
||
| 272 | } |
||
| 273 | |||
| 274 | return $classDefinition; |
||
| 275 | } |
||
| 276 | |||
| 277 | /** |
||
| 278 | * Create collection |
||
| 279 | * |
||
| 280 | * @param string $name name of collection |
||
| 281 | * @param array|null $options array of options |
||
| 282 | * @return \Sokil\Mongo\Collection |
||
| 283 | * @throws \Sokil\Mongo\Exception |
||
| 284 | */ |
||
| 285 | public function createCollection($name, array $options = null) |
||
| 286 | { |
||
| 287 | $classDefinition = $this->getCollectionDefinition($name); |
||
| 288 | |||
| 289 | if (!empty($options)) { |
||
| 290 | $classDefinition->merge($options); |
||
| 291 | } |
||
| 292 | |||
| 293 | $mongoCollection = $this->getMongoDB()->createCollection( |
||
| 294 | $name, |
||
| 295 | $classDefinition->getMongoCollectionOptions() |
||
| 296 | ); |
||
| 297 | |||
| 298 | // create collection |
||
| 299 | $className = $classDefinition->getClass(); |
||
| 300 | return new $className( |
||
| 301 | $this, |
||
| 302 | $mongoCollection, |
||
| 303 | $classDefinition |
||
| 304 | ); |
||
| 305 | } |
||
| 306 | |||
| 307 | /** |
||
| 308 | * |
||
| 309 | * @param string $name name of collection |
||
| 310 | * @param int $maxElements The maximum number of elements to store in the collection. |
||
| 311 | * @param int $size Size in bytes. |
||
| 312 | * @return \Sokil\Mongo\Collection |
||
| 313 | * @throws Exception |
||
| 314 | */ |
||
| 315 | public function createCappedCollection($name, $maxElements, $size) |
||
| 316 | { |
||
| 317 | $options = array( |
||
| 318 | 'capped' => true, |
||
| 319 | 'size' => (int) $size, |
||
| 320 | 'max' => (int) $maxElements, |
||
| 321 | ); |
||
| 322 | |||
| 323 | if (!$options['size'] && !$options['max']) { |
||
| 324 | throw new Exception('Size or number of elements must be defined'); |
||
| 325 | } |
||
| 326 | |||
| 327 | return $this->createCollection($name, $options); |
||
| 328 | } |
||
| 329 | |||
| 330 | /** |
||
| 331 | * |
||
| 332 | * @param string $name name of collection |
||
| 333 | * @return \Sokil\Mongo\Collection |
||
| 334 | * @throws \Sokil\Mongo\Exception |
||
| 335 | */ |
||
| 336 | View Code Duplication | public function getCollection($name) |
|
| 337 | { |
||
| 338 | // return from pool |
||
| 339 | if ($this->collectionPoolEnabled && isset($this->collectionPool[$name])) { |
||
| 340 | return $this->collectionPool[$name]; |
||
| 341 | } |
||
| 342 | |||
| 343 | // no object in pool - init new |
||
| 344 | $classDefinition = $this->getCollectionDefinition($name); |
||
| 345 | $className = $classDefinition->getClass(); |
||
| 346 | |||
| 347 | // create collection class |
||
| 348 | $collection = new $className($this, $name, $classDefinition); |
||
| 349 | if (!$collection instanceof Collection) { |
||
| 350 | throw new Exception('Must be instance of \Sokil\Mongo\Collection'); |
||
| 351 | } |
||
| 352 | |||
| 353 | // store to pool |
||
| 354 | if ($this->collectionPoolEnabled) { |
||
| 355 | $this->collectionPool[$name] = $collection; |
||
| 356 | } |
||
| 357 | |||
| 358 | // return |
||
| 359 | return $collection; |
||
| 360 | } |
||
| 361 | |||
| 362 | /** |
||
| 363 | * Get Document instance by it's reference |
||
| 364 | * |
||
| 365 | * @param array $ref reference to document |
||
| 366 | * @param bool $useDocumentPool try to get document from pool or fetch document from database |
||
| 367 | * |
||
| 368 | * @return Document|null |
||
| 369 | */ |
||
| 370 | View Code Duplication | public function getDocumentByReference(array $ref, $useDocumentPool = true) |
|
| 371 | { |
||
| 372 | $documentArray = $this->getMongoDB()->getDBRef($ref); |
||
| 373 | if (null === $documentArray) { |
||
| 374 | return null; |
||
| 375 | } |
||
| 376 | |||
| 377 | return $this->getCollection($ref['$ref'])->hydrate($documentArray, $useDocumentPool); |
||
| 378 | } |
||
| 379 | |||
| 380 | /** |
||
| 381 | * Get instance of GridFS |
||
| 382 | * |
||
| 383 | * @param string $name prefix of files and chunks collection |
||
| 384 | * @return \Sokil\Mongo\GridFS |
||
| 385 | * @throws \Sokil\Mongo\Exception |
||
| 386 | */ |
||
| 387 | View Code Duplication | public function getGridFS($name = 'fs') |
|
| 388 | { |
||
| 389 | // return from pool |
||
| 390 | if ($this->collectionPoolEnabled && isset($this->collectionPool[$name])) { |
||
| 391 | return $this->collectionPool[$name]; |
||
| 392 | } |
||
| 393 | |||
| 394 | // no object in pool - init new |
||
| 395 | $classDefinition = $this->getCollectionDefinition($name, array('gridfs' => true)); |
||
| 396 | $className = $classDefinition->getClass(); |
||
| 397 | |||
| 398 | // create collection class |
||
| 399 | $collection = new $className($this, $name, $classDefinition); |
||
| 400 | if (!$collection instanceof \Sokil\Mongo\GridFS) { |
||
| 401 | throw new Exception('Must be instance of \Sokil\Mongo\GridFS'); |
||
| 402 | } |
||
| 403 | |||
| 404 | // store to pool |
||
| 405 | if ($this->collectionPoolEnabled) { |
||
| 406 | $this->collectionPool[$name] = $collection; |
||
| 407 | } |
||
| 408 | |||
| 409 | // return |
||
| 410 | return $collection; |
||
| 411 | } |
||
| 412 | |||
| 413 | /** |
||
| 414 | * |
||
| 415 | * @param string $channel name of channel |
||
| 416 | * @return \Sokil\Mongo\Queue |
||
| 417 | */ |
||
| 418 | public function getQueue($channel) |
||
| 419 | { |
||
| 420 | return new Queue($this, $channel); |
||
| 421 | } |
||
| 422 | |||
| 423 | /** |
||
| 424 | * Get cache |
||
| 425 | * |
||
| 426 | * @param string $namespace name of collection to be created in database |
||
| 427 | * |
||
| 428 | * @return Cache |
||
| 429 | */ |
||
| 430 | public function getCache($namespace) |
||
| 431 | { |
||
| 432 | return new Cache($this, $namespace); |
||
| 433 | } |
||
| 434 | |||
| 435 | public function readPrimaryOnly() |
||
| 436 | { |
||
| 437 | $this->getMongoDB()->setReadPreference(\MongoClient::RP_PRIMARY); |
||
| 438 | return $this; |
||
| 439 | } |
||
| 440 | |||
| 441 | public function readPrimaryPreferred(array $tags = null) |
||
| 442 | { |
||
| 443 | $this->getMongoDB()->setReadPreference(\MongoClient::RP_PRIMARY_PREFERRED, $tags); |
||
| 444 | return $this; |
||
| 445 | } |
||
| 446 | |||
| 447 | public function readSecondaryOnly(array $tags = null) |
||
| 448 | { |
||
| 449 | $this->getMongoDB()->setReadPreference(\MongoClient::RP_SECONDARY, $tags); |
||
| 450 | return $this; |
||
| 451 | } |
||
| 452 | |||
| 453 | public function readSecondaryPreferred(array $tags = null) |
||
| 454 | { |
||
| 455 | $this->getMongoDB()->setReadPreference(\MongoClient::RP_SECONDARY_PREFERRED, $tags); |
||
| 456 | return $this; |
||
| 457 | } |
||
| 458 | |||
| 459 | public function readNearest(array $tags = null) |
||
| 460 | { |
||
| 461 | $this->getMongoDB()->setReadPreference(\MongoClient::RP_NEAREST, $tags); |
||
| 462 | return $this; |
||
| 463 | } |
||
| 464 | |||
| 465 | public function getReadPreference() |
||
| 466 | { |
||
| 467 | return $this->getMongoDB()->getReadPreference(); |
||
| 468 | } |
||
| 469 | |||
| 470 | /** |
||
| 471 | * Define write concern. |
||
| 472 | * May be used only if mongo extension version >=1.5 |
||
| 473 | * |
||
| 474 | * @param string|integer $w write concern |
||
| 475 | * @param int $timeout timeout in milliseconds |
||
| 476 | * @return \Sokil\Mongo\Database |
||
| 477 | * @throws \Sokil\Mongo\Exception |
||
| 478 | */ |
||
| 479 | public function setWriteConcern($w, $timeout = 10000) |
||
| 480 | { |
||
| 481 | if (!$this->getMongoDB()->setWriteConcern($w, (int) $timeout)) { |
||
| 482 | throw new Exception('Error setting write concern'); |
||
| 483 | } |
||
| 484 | |||
| 485 | return $this; |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * Define unacknowledged write concern. |
||
| 490 | * May be used only if mongo extension version >=1.5 |
||
| 491 | * |
||
| 492 | * @param int $timeout timeout in milliseconds |
||
| 493 | * @return \Sokil\Mongo\Database |
||
| 494 | */ |
||
| 495 | public function setUnacknowledgedWriteConcern($timeout = 10000) |
||
| 496 | { |
||
| 497 | $this->setWriteConcern(0, (int) $timeout); |
||
| 498 | return $this; |
||
| 499 | } |
||
| 500 | |||
| 501 | /** |
||
| 502 | * Define majority write concern. |
||
| 503 | * May be used only if mongo extension version >=1.5 |
||
| 504 | * |
||
| 505 | * @param int $timeout timeout in milliseconds |
||
| 506 | * @return \Sokil\Mongo\Database |
||
| 507 | */ |
||
| 508 | public function setMajorityWriteConcern($timeout = 10000) |
||
| 509 | { |
||
| 510 | $this->setWriteConcern('majority', (int) $timeout); |
||
| 511 | return $this; |
||
| 512 | } |
||
| 513 | |||
| 514 | /** |
||
| 515 | * Get current write concern |
||
| 516 | * May be used only if mongo extension version >=1.5 |
||
| 517 | * |
||
| 518 | * @return mixed |
||
| 519 | */ |
||
| 520 | public function getWriteConcern() |
||
| 521 | { |
||
| 522 | return $this->getMongoDB()->getWriteConcern(); |
||
| 523 | } |
||
| 524 | |||
| 525 | /** |
||
| 526 | * Execute Mongo command |
||
| 527 | * |
||
| 528 | * @param array $command |
||
| 529 | * @param array $options |
||
| 530 | * @return array |
||
| 531 | */ |
||
| 532 | public function executeCommand(array $command, array $options = array()) |
||
| 533 | { |
||
| 534 | return $this->getMongoDB()->command($command, $options); |
||
| 535 | } |
||
| 536 | |||
| 537 | public function executeJS($code, array $args = array()) |
||
| 538 | { |
||
| 539 | $response = $this->getMongoDB()->execute($code, $args); |
||
| 540 | if ($response['ok'] == 1.0) { |
||
| 541 | return $response['retval']; |
||
| 542 | } else { |
||
| 543 | throw new Exception('Error #' . $response['code'] . ': ' . $response['errmsg'], $response['code']); |
||
| 544 | } |
||
| 545 | } |
||
| 546 | |||
| 547 | public function stats() |
||
| 548 | { |
||
| 549 | return $this->executeCommand(array( |
||
| 550 | 'dbstats' => 1, |
||
| 551 | )); |
||
| 552 | } |
||
| 553 | |||
| 554 | public function getLastError() |
||
| 555 | { |
||
| 556 | return $this->getMongoDB()->lastError(); |
||
| 557 | } |
||
| 558 | |||
| 559 | public function getProfilerParams() |
||
| 560 | { |
||
| 561 | return $this->executeCommand(array( |
||
| 562 | 'profile' => -1, |
||
| 563 | )); |
||
| 564 | } |
||
| 565 | |||
| 566 | public function getProfilerLevel() |
||
| 567 | { |
||
| 568 | $params = $this->getProfilerParams(); |
||
| 569 | return $params['was']; |
||
| 570 | } |
||
| 571 | |||
| 572 | public function getProfilerSlowMs() |
||
| 573 | { |
||
| 574 | $params = $this->getProfilerParams(); |
||
| 575 | return $params['slowms']; |
||
| 576 | } |
||
| 577 | |||
| 578 | public function disableProfiler() |
||
| 579 | { |
||
| 580 | return $this->executeCommand(array( |
||
| 581 | 'profile' => 0, |
||
| 582 | )); |
||
| 583 | } |
||
| 584 | |||
| 585 | public function profileSlowQueries($slowms = 100) |
||
| 586 | { |
||
| 587 | return $this->executeCommand(array( |
||
| 588 | 'profile' => 1, |
||
| 589 | 'slowms' => (int) $slowms |
||
| 590 | )); |
||
| 591 | } |
||
| 592 | |||
| 593 | public function profileAllQueries($slowms = null) |
||
| 594 | { |
||
| 595 | $command = array( |
||
| 596 | 'profile' => 2, |
||
| 597 | ); |
||
| 598 | |||
| 599 | if ($slowms) { |
||
| 600 | $command['slowms'] = (int) $slowms; |
||
| 601 | } |
||
| 602 | |||
| 603 | return $this->executeCommand($command); |
||
| 604 | } |
||
| 605 | |||
| 606 | /** |
||
| 607 | * |
||
| 608 | * @return \Sokil\Mongo\Cursor |
||
| 609 | */ |
||
| 610 | public function findProfilerRows() |
||
| 611 | { |
||
| 612 | return $this |
||
| 613 | ->getCollection('system.profile') |
||
| 614 | ->find() |
||
| 615 | ->asArray(); |
||
| 616 | } |
||
| 617 | } |
||
| 618 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.