256 lines
8.5 KiB
PHP
256 lines
8.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Lib;
|
||
|
|
|
||
|
|
use App\Enums\MediaExtension;
|
||
|
|
use getID3;
|
||
|
|
use Illuminate\Support\Str;
|
||
|
|
use Imagick;
|
||
|
|
use ImagickException;
|
||
|
|
|
||
|
|
class ToolboxHelper
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* Get ID3 Play time string
|
||
|
|
*
|
||
|
|
* @param string $filepath
|
||
|
|
* @return string|null
|
||
|
|
*/
|
||
|
|
public static function getId3Playtime(string $filepath): ?string
|
||
|
|
{
|
||
|
|
$getID3 = new getID3();
|
||
|
|
|
||
|
|
$results = $getID3->analyze($filepath);
|
||
|
|
|
||
|
|
if (array_key_exists('error', $results)) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
if (array_key_exists('playtime_string', $results)) {
|
||
|
|
return \strval($results['playtime_string']);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get ID3 video dimensions
|
||
|
|
*
|
||
|
|
* @param string $filepath
|
||
|
|
* @return array|null Containing 'width' and 'height'
|
||
|
|
*/
|
||
|
|
public static function getId3VideoDimensions(string $filepath): ?array
|
||
|
|
{
|
||
|
|
$getID3 = new getID3();
|
||
|
|
|
||
|
|
$results = $getID3->analyze($filepath);
|
||
|
|
|
||
|
|
if (array_key_exists('error', $results)) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
if (isset($results['video']['resolution_x']) and isset($results['video']['resolution_y'])) {
|
||
|
|
return [
|
||
|
|
'width' => \intval($results['video']['resolution_x']),
|
||
|
|
'height' => \intval($results['video']['resolution_y'])
|
||
|
|
];
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* resolution in pixels per centimeter
|
||
|
|
*
|
||
|
|
* @param string $filepath
|
||
|
|
* @return array|null Containing 'resolutionX', 'resolutionY', 'width' and 'height'
|
||
|
|
*/
|
||
|
|
public static function getImageDimensions(string $filepath): ?array
|
||
|
|
{
|
||
|
|
$ext = \pathinfo($filepath, \PATHINFO_EXTENSION);
|
||
|
|
try {
|
||
|
|
$im = new Imagick($filepath);
|
||
|
|
if (
|
||
|
|
in_array($ext, [
|
||
|
|
MediaExtension::pdf->value,
|
||
|
|
MediaExtension::ai->value,
|
||
|
|
MediaExtension::psd->value,
|
||
|
|
MediaExtension::gif->value
|
||
|
|
])
|
||
|
|
) {
|
||
|
|
$im->setIteratorIndex(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
$r = $im->getImageResolution();
|
||
|
|
$u = $im->getImageUnits();
|
||
|
|
|
||
|
|
// Force image resolution to pixel per centimeters.
|
||
|
|
if ($u == Imagick::RESOLUTION_PIXELSPERINCH) {
|
||
|
|
$r['x'] = (int)round($r['x'] / 2.54);
|
||
|
|
$r['y'] = (int)round($r['y'] / 2.54);
|
||
|
|
$im->setImageUnits(Imagick::RESOLUTION_PIXELSPERCENTIMETER);
|
||
|
|
$im->setImageResolution($r['x'], $r['y']);
|
||
|
|
|
||
|
|
// Note that the number type is double again.
|
||
|
|
$r = $im->getImageResolution();
|
||
|
|
}
|
||
|
|
|
||
|
|
$imageprops = $im->getImageGeometry();
|
||
|
|
$im->destroy();
|
||
|
|
return [
|
||
|
|
'resolutionX' => \intval($r['x']),
|
||
|
|
'resolutionY' => \intval($r['y']),
|
||
|
|
'width' => \intval($imageprops['width']),
|
||
|
|
'height' => \intval($imageprops['height'])
|
||
|
|
];
|
||
|
|
} catch (ImagickException $e) {
|
||
|
|
report($e);
|
||
|
|
} //end try
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Sanitize File name for uploaded files
|
||
|
|
*
|
||
|
|
* @param string $filename The original uploaded client file name.
|
||
|
|
* @param boolean $beautify Beautify the filename.
|
||
|
|
* @return string The sanitized file name.
|
||
|
|
*/
|
||
|
|
public static function sanitizeFileName(string $filename, bool $beautify = true): string
|
||
|
|
{
|
||
|
|
// * Sanitize filename
|
||
|
|
// phpcs:disable Generic.Files.LineLength.TooLong
|
||
|
|
$filename = preg_replace(
|
||
|
|
'~
|
||
|
|
[<>:"/\\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
|
||
|
|
[\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
|
||
|
|
[\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
|
||
|
|
[#\[\]@!$&\'()+,;=]| # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
|
||
|
|
[{}^\~`] # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
|
||
|
|
~x',
|
||
|
|
'-',
|
||
|
|
$filename
|
||
|
|
);
|
||
|
|
// phpcs:enable
|
||
|
|
// * Avoids '.' '..' or '.hiddenFiles' .
|
||
|
|
$filename = Str::of($filename)->ltrim('.-');
|
||
|
|
// * Optional beautification
|
||
|
|
if ($beautify) {
|
||
|
|
$filename = static::beautifyFilename($filename);
|
||
|
|
}
|
||
|
|
// * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
|
||
|
|
/** @var string */
|
||
|
|
$extension = pathinfo($filename, \PATHINFO_EXTENSION);
|
||
|
|
/** @var string */
|
||
|
|
$filename = pathinfo($filename, \PATHINFO_FILENAME);
|
||
|
|
|
||
|
|
$encoding = mb_detect_encoding($filename);
|
||
|
|
$encoding = $encoding ? $encoding : null;
|
||
|
|
return \mb_strcut(
|
||
|
|
$filename,
|
||
|
|
0,
|
||
|
|
255 - (!empty($extension) ? strlen($extension) + 1 : 0),
|
||
|
|
$encoding
|
||
|
|
) . (!empty($extension) ? ".$extension" : '');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Beautify a filename
|
||
|
|
*
|
||
|
|
* @param string $filename
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
private static function beautifyFilename(string $filename): string
|
||
|
|
{
|
||
|
|
// Reduce consecutive characters.
|
||
|
|
$filename = preg_replace([
|
||
|
|
// * "file name.zip" becomes 'file-name.zip'
|
||
|
|
'/ +/',
|
||
|
|
// * "file___name.zip" becomes 'file-name.zip'
|
||
|
|
'/_+/',
|
||
|
|
// * "file---name.zip" becomes 'file-name.zip'
|
||
|
|
'/-+/'
|
||
|
|
], '-', $filename);
|
||
|
|
$filename = preg_replace([
|
||
|
|
// * "file--.--.-.--name.zip" becomes "file.name.zip"
|
||
|
|
'/-*\.-*/',
|
||
|
|
// * "file...name..zip" becomes "file.name.zip"
|
||
|
|
'/\.{2,}/'
|
||
|
|
], '.', $filename);
|
||
|
|
// Lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 .
|
||
|
|
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
|
||
|
|
// * ".file-name.-" becomes "file-name"
|
||
|
|
$filename = trim($filename, '.-');
|
||
|
|
return $filename;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert file size to human readable string
|
||
|
|
*
|
||
|
|
* @param mixed $octet
|
||
|
|
* @param integer $precision
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public static function formatBytes(mixed $octet, int $precision = 2): string
|
||
|
|
{
|
||
|
|
// * Array contenant les differents unités
|
||
|
|
$unite = ['octet', 'Kio', 'Mio', 'Gio'];
|
||
|
|
|
||
|
|
$octet = \floatval($octet);
|
||
|
|
|
||
|
|
$Kio = 1024;
|
||
|
|
$Mio = 1024 * $Kio;
|
||
|
|
$Gio = 1024 * $Mio;
|
||
|
|
|
||
|
|
switch (true) {
|
||
|
|
// * octet
|
||
|
|
case $octet < $Kio:
|
||
|
|
return $octet . $unite[0];
|
||
|
|
// * kio
|
||
|
|
case $octet < $Mio:
|
||
|
|
return $octet . $unite[1];
|
||
|
|
// * Mio
|
||
|
|
case $octet < $Gio:
|
||
|
|
return \round($octet / $Mio, $precision) . $unite[2];
|
||
|
|
// * Gio
|
||
|
|
default:
|
||
|
|
// * Gio
|
||
|
|
return \round($octet / $Gio, $precision) . $unite[3];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* * Register CSP report URI
|
||
|
|
*
|
||
|
|
* Put this function inside RouteServiceProvider,
|
||
|
|
* and call it in RouteServiceProvider::boot
|
||
|
|
*
|
||
|
|
* ! Use this in conjonction with kwaadpepper/exception-handler
|
||
|
|
*
|
||
|
|
* @return void
|
||
|
|
*/
|
||
|
|
public static function cspReportRoute()
|
||
|
|
{
|
||
|
|
// * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware
|
||
|
|
\Illuminate\Support\Facades\Route::post('/csp-report', function () {
|
||
|
|
$isCspRequest = request()->header('Content-Type') === 'application/csp-report';
|
||
|
|
if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) {
|
||
|
|
abort(401);
|
||
|
|
}
|
||
|
|
// * Flatten if needed
|
||
|
|
$cspReport = collect(request()->json('csp-report'))
|
||
|
|
->flatMap(function ($v, $k) {
|
||
|
|
return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v];
|
||
|
|
})->all();
|
||
|
|
// * Filter array keys
|
||
|
|
$cspReport = collect(array_intersect_key($cspReport, [
|
||
|
|
'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '',
|
||
|
|
'referrer' => '', 'violated-directive' => ''
|
||
|
|
]));
|
||
|
|
report(new \Illuminate\Http\Client\HttpClientException(sprintf(
|
||
|
|
"CSP REPORT for %s : \n %s",
|
||
|
|
$cspReport->get('document-uri'),
|
||
|
|
$cspReport->implode("\n")
|
||
|
|
)));
|
||
|
|
})->middleware('throttle:5,2')
|
||
|
|
->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class)
|
||
|
|
->name('cspReportUri');
|
||
|
|
}
|
||
|
|
}
|