Issues (9)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Btrieve.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace FForattini\Btrieve;
3
4
use FForattini\Btrieve\Bin;
5
use FForattini\Btrieve\Hex;
6
use FForattini\Btrieve\Str;
7
use InvalidArgumentException;
8
use FForattini\Btrieve\Rule\SkipRule;
9
use FForattini\Btrieve\Column\IdColumn;
10
use FForattini\Btrieve\Column\IntColumn;
11
use FForattini\Btrieve\Column\ColumnRule;
12
use FForattini\Btrieve\Column\DateColumn;
13
use FForattini\Btrieve\Column\FloatColumn;
14
use FForattini\Btrieve\Rule\RuleInterface;
15
use FForattini\Btrieve\Column\StringColumn;
16
use FForattini\Btrieve\Persistence\RowsPool;
17
use FForattini\Btrieve\Persistence\DebugPool;
18
use FForattini\Btrieve\Column\ColumnInterface;
19
use FForattini\Btrieve\Persistence\RecordsPool;
20
use FForattini\Btrieve\Persistence\BtrieveRecord;
21
use FForattini\Btrieve\Persistence\DatasetInterface;
22
23
class Btrieve
24
{
25
	protected $input;
26
	protected $input_stats;
27
	protected $rules;
28
	protected $pointer;
29
	protected $columns;
30
	protected $dirty;
31
	protected $variable_column_name;
32
33
	public $pools;
34
	const POOL_MAIN 	= 0;
35
	const POOL_DEBUG 	= 1;
36
	const POOL_ROWS 	= 2;
37
38
	/**
39
	 * Creates a Btrieve parser from a file.
40
	 * @param  string $file Full path
41
	 * @return Btrieve
42
	 */
43
	public static function load($file)
44
	{
45
		return (new self)->setinput(realpath($file));
46
	}
47
48
	/**
49
	 * Constructs a Btrieve parser
50
	 */
51
	public function __construct()
52
	{
53
		$this->setRules([]);
54
55
		$this->registerPool(new RecordsPool, self::POOL_MAIN);
56
		$this->registerPool(new DebugPool, self::POOL_DEBUG);
57
		$this->registerPool(new RowsPool, self::POOL_ROWS);
58
		$this->initPools();
59
60
		$this->dirty = false;
61
62
		$this->setVariableColumnName('attach');
63
	}
64
65
	/**
66
	 * Set input Btrieve file.
67
	 * @param string $file Real path
68
	 * @return Btrieve
69
	 */
70
	public function setInput($file)
71
	{
72
		if(!file_exists($file)) {
73
			throw new InvalidArgumentException('File not found.');
74
		}
75
		$this->input = $file;
76
		return $this->begin();
77
	}
78
79
	/**
80
	 * Get input Btrieve file.
81
	 * @return string Real path
82
	 */
83
	public function getInput()
84
	{
85
		return $this->input;
86
	}
87
88
	/**
89
	 * Set rules for the Btrieve parser
90
	 * @param array $rules
91
	 * @return Btrieve
92
	 */
93
	public function setRules($rules)
94
	{
95
		if(!is_array($rules)) {
96
			$rules = [$rules];
97
		}
98
99
		$this->rules = $rules;
100
		$this->dirty = true;
101
		return $this;
102
	}
103
104
	/**
105
	 * Get rules from the Btrieve parser
106
	 * @return array
107
	 */
108
	public function getRules()
109
	{
110
		return $this->rules;
111
	}
112
113
	/**
114
	 * Count the number of rules already added.
115
	 * @return int Number of rules.
116
	 */
117
	public function countRules()
118
	{
119
		return count($this->getRules());
120
	}
121
122
	/**
123
	 * Add a new Rule
124
	 * @param RuleInterface $rule
125
	 * @return Btrieve
126
	 */
127
	public function addRule(RuleInterface $rule)
128
	{
129
		$this->rules[] = $rule;
130
		$this->dirty = true;
131
		return $this;
132
	}
133
134
	/**
135
	 * Add an array of Rules.
136
	 * @param array $new_rules
137
	 * @return Btrieve
138
	 */
139
	public function addRules($new_rules)
140
	{
141
		if(!is_array($new_rules)) {
142
			throw new InvalidArgumentException('Parameter must be an array of Rules');
143
		}
144
145
		foreach ($new_rules as $new_rule) {
146
			$this->addRule($new_rule);
147
		}
148
149
		return $this;
150
	}
151
152
	/**
153
	 * Romove all rules.
154
	 * @return Btrieve
155
	 */
156
	public function clearRules()
157
	{
158
		foreach($this->getRules() as $rule) {
159
			unset($rule);
160
		}
161
		unset($this->rules);
162
		$this->setRules([]);
163
		return $this;
164
	}
165
166
	/**
167
	 * Adds a Skip to the rules.
168
	 * @param  string $title
169
	 * @param  int $size
170
	 * @return Btrieve
171
	 */
172
	public function skip($size = null)
173
	{
174
		if(is_null($size)) {
175
			$this->addRule(new SkipRule());
176
		} else {
177
			$this->addRule(new SkipRule($size));
178
		}
179
		return $this;
180
	}
181
182
	/**
183
	 * Adds a column to the rules.
184
	 * @param ColumnInterface $column
185
	 * @return Btrieve
186
	 */
187
	public function addColumn(ColumnInterface $column)
188
	{
189
		$this->addrule($column);
190
		return $this;
191
	}
192
193
	/**
194
	 * Adds a IdColumn to the rules.
195
	 * @param  string $title
196
	 * @param  int $size
197
	 * @return Btrieve
198
	 */
199
	public function id($title, $size = null)
200
	{
201
		if(is_null($size)) {
202
			$this->addColumn(new IdColumn($title));
203
		} else {
204
			$this->addColumn(new IdColumn($title, $size));
205
		}
206
		return $this;
207
	}
208
209
	/**
210
	 * Adds a IntColumn to the rules.
211
	 * @param  string $title
212
	 * @param  int $size
213
	 * @return Btrieve
214
	 */
215
	public function int($title, $size = null)
216
	{
217
		if(is_null($size)) {
218
			$this->addColumn(new IntColumn($title));
219
		} else {
220
			$this->addColumn(new IntColumn($title, $size));
221
		}
222
		return $this;
223
	}
224
225
	/**
226
	 * Adds a DateColumn to the rules.
227
	 * @param  string $title
228
	 * @param  int $size
229
	 * @return Btrieve
230
	 */
231
	public function date($title, $size = null)
232
	{
233
		if(is_null($size)) {
234
			$this->addColumn(new DateColumn($title));
235
		} else {
236
			$this->addColumn(new DateColumn($title, $size));
237
		}
238
		return $this;
239
	}
240
241
	/**
242
	 * Adds a FloatColumn to the rules.
243
	 * @param  string $title
244
	 * @param  int $size
245
	 * @return Btrieve
246
	 */
247
	public function float($title, $size = null)
248
	{
249
		if(is_null($size)) {
250
			$this->addColumn(new FloatColumn($title));
251
		} else {
252
			$this->addColumn(new FloatColumn($title, $size));
253
		}
254
		return $this;
255
	}
256
257
	/**
258
	 * Adds a StringColumn to the rules.
259
	 * @param  string $title
260
	 * @param  int $size
261
	 * @return Btrieve
262
	 */
263
	public function string($title, $size = null)
264
	{
265
		if(is_null($size)) {
266
			$this->addColumn(new StringColumn($title));
267
		} else {
268
			$this->addColumn(new StringColumn($title, $size));
269
		}
270
		return $this;
271
	}
272
273
	/**
274
	 * Sets a name for the variable column.
275
	 * @param string $name
276
	 */
277
	public function setVariableColumnName($name)
278
	{
279
		$this->variable_column_name = $name;
280
		return $this;
281
	}
282
283
	/**
284
	 * Sets a name for the variable column.
285
	 * @param string $name
286
	 */
287
	public function getVariableColumnName()
288
	{
289
		return $this->variable_column_name;
290
	}
291
292
	/**
293
	 * Read all the rules and returns
294
	 * @return void
295
	 */
296
	public function setColumns()
297
	{
298
		$columns = array_filter($this->getRules(), function($rule) {
299
			if($rule instanceof ColumnInterface) {
300
				return $rule;
301
			}
302
		});
303
304
		$columns = array_map(function(ColumnInterface $rule) {
305
			return $rule->getTitle();
306
		}, $columns);
307
308
		$this->columns = $columns;
309
	}
310
311
	/**
312
	 * Returns an array of columns names.
313
	 * @return array
314
	 */
315
	public function getColumns()
316
	{
317
		if($this->dirty) {
318
			$this->setColumns();
319
			$this->dirty = false;
320
		}
321
322
		return $this->columns;
323
	}
324
325
	/**
326
	 * Get current pointer over Betrieve's input file.
327
	 * @return resource
328
	 */
329
	public function getPointer()
330
	{
331
		return $this->pointer;
332
	}
333
334
	/**
335
	 * Creates a pointer at the begin of input Btrieve file.
336
	 * @return Btrieve
337
	 */
338
	public function begin()
339
	{
340
		$this->pointer = fopen($this->input,'rb');
341
		$this->input_stats = fstat($this->getPointer());
342
343
		if(is_null($this->getPointer())) {
344
			throw new Exception('Couldnt create the pointer at input file.');
345
		}
346
347
		return $this;
348
	}
349
350
	/**
351
	 * Returns Btrieve input file length
352
	 * @return int
353
	 */
354
	public function inputLength()
355
	{
356
		return $this->input_stats['size'];
357
	}
358
359
	/**
360
	 * Register a new pool of elements;
361
	 * @param  DatasetInterface $pool
362
	 * @param  int $key
363
	 * @return Btrieve
364
	 */
365
	public function registerPool(DatasetInterface $pool, $key)
366
	{
367
		$this->pools[$key] = $pool;
368
		return $this;
369
	}
370
371
	/**
372
	 * Verify if Pool was registered.
373
	 * @param  int  $index
374
	 * @return boolean
375
	 */
376
	public function hasPool($index)
377
	{
378
		if(isset($this->pools[$index])) {
379
			return true;
380
		}
381
		return false;
382
	}
383
384
	/**
385
	 * Get targered pool by index.
386
	 * @param  int $index
387
	 * @return DatasetInterface
388
	 */
389
	public function getPool($key)
390
	{
391
		if($this->hasPool($key)) {
392
			return $this->pools[$key];
393
		}
394
		throw new InvalidArgumentException('Pool with key \''.$key.'\' was not registered.');
395
	}
396
397
	/**
398
	 * Get array of registered pools.
399
	 * @return array
400
	 */
401
	public function getPools()
402
	{
403
		return $this->pools;
404
	}
405
406
	/**
407
	 * Initializes the pool of elements.
408
	 * @param  int $target_pool
409
	 * @return Btrieve
410
	 */
411
	public function initPool($target_pool = self::POOL_MAIN)
412
	{
413
		$this->getPool($target_pool)->init();
414
		return $this;
415
	}
416
417
	/**
418
	 * Initializes all pools registered.
419
	 * @return Btrieve
420
	 */
421
	public function initPools()
422
	{
423
		foreach (array_keys($this->getPools()) as $key) {
424
			$this->initPool($key);
425
		}
426
		return $this;
427
	}
428
429
	/**
430
	 * Remove all elements from a pool of elements.
431
	 * @param  int $target_pool
432
	 * @return Btrieve
433
	 */
434
	public function clearPool($target_pool = self::POOL_MAIN)
435
	{
436
		$this->getPool($target_pool)->reset();
437
		return $this;
438
	}
439
440
	/**
441
	 * Clear all registered pools.
442
	 * @return Btrieve
443
	 */
444
	public function clearPools()
445
	{
446
		foreach (array_keys($this->getPools()) as $key) {
447
			$this->clearPool($key);
448
		}
449
		return $this;
450
	}
451
452
	/**
453
	 * Adds a record to one of the pools.
454
	 * @param mixed $record
455
	 * @return Btrieve
456
	 */
457
	protected function addElem($element)
458
	{
459
		foreach($this->getPools() as $pool) {
460
			$pool->add($element);
461
		}
462
		return $this;
463
	}
464
465
	/**
466
	 * Returns if the reader can se if it has more elements or not.
467
	 * @return boolean
468
	 */
469
	public function hasNext()
470
	{
471
		if($this->position() == $this->inputLength() and feof($this->getPointer())) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
472
			return false;
473
		}
474
		return true;
475
	}
476
477
	/**
478
	 * Returns Btrieve's pointer position at the input file
479
	 * @return int
480
	 */
481
	public function position()
482
	{
483
		return ftell($this->getPointer());
484
	}
485
486
	/**
487
	 * Reads next element using the rules set starting from last pointers position.
488
	 * @return object
489
	 */
490
	public function next()
491
	{
492
		if(!$this->hasNext()) {
493
			return null;
494
		}
495
		$size = $this->nextRecordLength();
496
		if($size < 1) {
497
			return null;
498
		}
499
		$raw_content = $this->readBits($size);
500
		if(is_null($raw_content)) {
501
			return null;
502
		}
503
		$index = $this->applyRules($raw_content, $size);
504
	}
505
506
	/**
507
	 * Read number of bits from the input file.
508
	 * @param  int $n
509
	 * @return string
510
	 */
511
	protected function readBits($n = 1)
512
	{
513
		if($this->position() + $n >= $this->inputLength() and feof($this->getPointer())) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
514
			return null;
515
		}
516
		return fread($this->getPointer(), $n);
517
	}
518
519
	/**
520
	 * Gets the next record length in bits
521
	 * @return int
522
	 */
523
	protected function nextRecordLength()
524
	{
525
		list($size, $temp) = ['', ''];
526
		
527
		while ($temp != "," and $this->hasNext()) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
528
			$size .= $temp;
529
			$temp = Bin::toStr($this->readBits());
530
		}
531
532
		return intval($size);
533
	}
534
535
	/**
536
	 * Gets a raw line and apply all declared rules to the object.
537
	 * @param  string $raw  String result of the specified length declared at beginning.
538
	 * @param  int $size Length of the string
539
	 * @return Btrieve
540
	 */
541
	protected function applyRules($raw, $size)
542
	{
543
		$position = 0;
544
		$attributes = [];
545
546
		foreach ($this->getRules() as $rule) {
547
			$column_content = substr($raw, $position, $rule->getLength());
548
			if($rule instanceof ColumnInterface) {
549
				$attributes[$rule->getTitle()] = $rule::cast($column_content);
550
			}
551
			$position += $rule->getLength();
552
		}
553
	
554
		if($position < $size) {
555
			$column_content = Bin::toHex(substr($raw, $position, ($size-$position)));
556
			$column_content = preg_split('/(?<=2020)2020(?!2020])/', $column_content);
557
			$column_content = Hex::toStr(end($column_content));
558
			$attributes[$this->getVariableColumnName()] = $column_content;
559
		}
560
561
		return $this->addElem($attributes);
562
	}
563
564
	/**
565
	 * Verify if the element exists
566
	 * @param  int $index
567
	 * @param  int $target_pool [description]
568
	 * @return bool
569
	 */
570
	public function exists($index, $target_pool = self::POOL_MAIN)
571
	{
572
		if($this->haspool($index)) {
573
			return $this->getPool($key)->exists($index);
574
		}
575
		throw new InvalidArgumentException('Invalid pool.');
576
	}
577
578
	/**
579
	 * Returns an element from specific position.
580
	 * @param  int $index
581
	 * @param  int $target_pool
582
	 * @return mixed
583
	 */
584
	public function elem($index, $target_pool = self::POOL_MAIN)
585
	{
586
		if($this->haspool($index)) {
587
			return $this->getPool($target_pool)->elem($index);
588
		} else {
589
			throw new InvalidArgumentException('Invalid pool.');
590
		}
591
		return null;
592
	}
593
594
	/**
595
	 * Get the first element.
596
	 * @param  int $target_pool
597
	 * @return mixed
598
	 */
599
	public function first($target_pool = self::POOL_MAIN)
600
	{
601
		return $this->elem(0, $target_pool);
602
	}
603
604
	/**
605
	 * Take a [n] ammounts of elements from the file and add to the pool.
606
	 * @param  integer $n Number of elements to read.
607
	 * @return Btrieve;
608
	 */
609
	public function take($number_of_elements = 1)
610
	{
611
		$elements = [];
612
		while(($number_of_elements > 0) and $this->hasNext())
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
613
		{
614
			$temp = $this->next();
615
			$objects->push($temp);
616
			$n--;
617
		}
618
619
		return $objects->all();
620
	}
621
622
	/**
623
	 * Read the whole input file and converts.
624
	 * @return Btrieve
625
	 */
626
	public function all() {
627
		$objects = []
628
		while($this->hasNext()) {
0 ignored issues
show
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_WHILE
Loading history...
629
			$next = $this->next();
630
			if($next == FALSE) break;
631
			$objects->push($next);
632
		}
633
634
		return $objects;
635
	}
636
637
}