PluginProbe
Packeta / 2.3.2
Packeta v2.3.2
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / src / Packetery / Module / Log / LogSizeLimiter.php

LogSizeLimiter.php in Packeta 2.3.2, at src/Packetery/Module/Log/LogSizeLimiter.php

134 lines 3.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare( strict_types=1 );
4
5 namespace Packetery\Module\Log;
6
7 use DateTimeImmutable;
8 use DateTimeZone;
9
10 class LogSizeLimiter {
11 /** @var int */
12 private $logLimitSizeBytes;
13
14 /** @var int */
15 private $logLimitDays;
16
17 public function __construct(
18 int $logLimitSizeBytes,
19 int $logLimitDays
20 ) {
21 $this->logLimitSizeBytes = $logLimitSizeBytes;
22 $this->logLimitDays = $logLimitDays;
23 }
24
25 public function getLimitedFilePartAsString( string $logPath ): ?string {
26 if ( $this->logLimitSizeBytes <= 0 || $this->logLimitDays <= 0 || ! is_file( $logPath ) ) {
27 return null;
28 }
29
30 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
31 $handle = fopen( $logPath, 'rb' );
32 if ( $handle === false ) {
33 return null;
34 }
35
36 $state = new LogSizeLimiterState( [], 0, [], null );
37
38 // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
39 while ( $line = fgets( $handle ) ) {
40 $matches = [];
41 if ( preg_match( '/^\[(.+?)]/', $line, $matches ) === 1 ) {
42 $timestamp = null;
43 if ( $this->isStrToTimeTokenValid( $matches[1] ) ) {
44 $maybeTimestamp = strtotime( $matches[1] );
45 $timestamp = $maybeTimestamp !== false ? $maybeTimestamp : null;
46 }
47 if ( $timestamp !== null ) {
48 $this->flushCurrentRecord( $state );
49 $state->setCurrentRecordLines( [ $line ] );
50 $state->setCurrentRecordTime( $timestamp );
51
52 continue;
53 }
54 }
55 if ( $state->getCurrentRecordTime() !== null ) {
56 $state->addCurrentRecordLine( $line );
57 }
58 }
59
60 $this->flushCurrentRecord( $state );
61
62 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
63 fclose( $handle );
64
65 if ( $state->getRecordQueue() === [] ) {
66 return null;
67 }
68
69 return implode( '', $state->getRecordQueue() );
70 }
71
72 /**
73 * Flushes the currently built record into the queue if it passes age and size checks.
74 */
75 private function flushCurrentRecord( LogSizeLimiterState $state ): void {
76 if ( $state->getCurrentRecordTime() === null || $state->getCurrentRecordLines() === [] ) {
77 $state->emptyCurrentRecordLines();
78
79 return;
80 }
81 if ( $state->getCurrentRecordTime() < $this->getCutoffTimestamp() ) {
82 $state->emptyCurrentRecordLines();
83
84 return;
85 }
86 $recordContent = implode( '', $state->getCurrentRecordLines() );
87 $recordLength = strlen( $recordContent );
88 if ( $recordLength > $this->logLimitSizeBytes ) {
89 $state->emptyCurrentRecordLines();
90
91 return;
92 }
93 $state->addRecordToQueue( $recordContent );
94 $state->addToTotalSize( $recordLength );
95 while ( $state->getTotalSize() > $this->logLimitSizeBytes && $state->getRecordQueue() !== [] ) {
96 $removed = $state->shiftRecordQueue();
97 $state->subtractFromTotalSize( is_string( $removed ) ? strlen( $removed ) : 0 );
98 }
99 $state->emptyCurrentRecordLines();
100 }
101
102 /**
103 * Validates that the token to be passed to strtotime contains only allowed characters.
104 * Allows digits, letters, plus, minus, colon, space, slash, dot, comma, underscore and parentheses.
105 */
106 private function isStrToTimeTokenValid( string $possibleTimeString ): bool {
107 $possibleTimeString = trim( $possibleTimeString );
108 if ( $possibleTimeString === '' ) {
109 return false;
110 }
111
112 return preg_match( '/^[0-9A-Za-z\-:+\s\/.,_()]+$/', $possibleTimeString ) === 1;
113 }
114
115 private function getCutoffTimestamp(): int {
116 $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
117
118 return $now->getTimestamp() - ( $this->logLimitDays * DAY_IN_SECONDS );
119 }
120
121 public function isFileFreshEnough( string $filePath ): bool {
122 if ( $this->logLimitDays <= 0 || ! is_file( $filePath ) ) {
123 return false;
124 }
125
126 $fileModificationTime = filemtime( $filePath );
127 if ( $fileModificationTime === false ) {
128 return false;
129 }
130
131 return $fileModificationTime >= $this->getCutoffTimestamp();
132 }
133 }
134