Skip to content

Commit f356d72

Browse files
committed
Refactor core helpers into traits and streamline Snyk workflow
1 parent 8f45d1f commit f356d72

11 files changed

Lines changed: 322 additions & 227 deletions

File tree

.github/workflows/snyk-security.yml

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,50 +30,33 @@ permissions:
3030
jobs:
3131
snyk:
3232
permissions:
33-
contents: read # for actions/checkout to fetch code
34-
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
35-
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
33+
contents: read
34+
security-events: write
35+
actions: read
3636
runs-on: ubuntu-latest
37+
env:
38+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
3739
steps:
3840
- uses: actions/checkout@v4
3941
- name: Set up Snyk CLI to check for security issues
40-
# Snyk can be used to break the build when it detects security issues.
41-
# In this case we want to upload the SAST issues to GitHub Code Scanning
4242
uses: snyk/actions/setup@806182742461562b67788a64410098c9d9b96adb
4343

44-
# For Snyk Open Source you must first set up the development environment for your application's dependencies
45-
# For example for Node
46-
#- uses: actions/setup-node@v4
47-
# with:
48-
# node-version: 20
49-
50-
env:
51-
# This is where you will need to introduce the Snyk API token created with your Snyk account
52-
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
53-
54-
# Runs Snyk Code (SAST) analysis and uploads result into GitHub.
55-
# Use || true to not fail the pipeline
5644
- name: Snyk Code test
57-
run: snyk code test --sarif > snyk-code.sarif # || true
45+
run: snyk code test --sarif > snyk-code.sarif
5846

59-
# Runs Snyk Open Source (SCA) analysis and uploads result to Snyk.
6047
- name: Snyk Open Source monitor
6148
run: snyk monitor --all-projects
6249

63-
# Runs Snyk Infrastructure as Code (IaC) analysis and uploads result to Snyk.
64-
# Use || true to not fail the pipeline.
6550
- name: Snyk IaC test and report
66-
run: snyk iac test --report # || true
51+
run: snyk iac test --report
6752

68-
# Build the docker image for testing
6953
- name: Build a Docker image
7054
run: docker build -t your/image-to-test .
71-
# Runs Snyk Container (Container and SCA) analysis and uploads result to Snyk.
55+
7256
- name: Snyk Container monitor
7357
run: snyk container monitor your/image-to-test --file=Dockerfile
7458

75-
# Push the Snyk Code results into GitHub Code Scanning tab
7659
- name: Upload result to GitHub Code Scanning
7760
uses: github/codeql-action/upload-sarif@v3
7861
with:
79-
sarif_file: snyk-code.sarif
62+
sarif_file: snyk-code.sarif

src/base/config/Config.php

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PSFS\base\Request;
88
use PSFS\base\Security;
99
use PSFS\base\types\helpers\Inspector;
10+
use PSFS\base\types\traits\Config\ConfigPersistenceTrait;
1011
use PSFS\base\types\traits\SingletonTrait;
1112
use PSFS\base\types\traits\TestTrait;
1213

@@ -17,6 +18,7 @@ class Config
1718
{
1819
use SingletonTrait;
1920
use TestTrait;
21+
use ConfigPersistenceTrait;
2022

2123
const DEFAULT_LANGUAGE = 'en';
2224
const DEFAULT_ENCODE = 'UTF-8';
@@ -150,42 +152,6 @@ public function isLoaded()
150152
return !empty($this->config);
151153
}
152154

153-
/**
154-
* @param array $data
155-
* @param array $extra
156-
* @return array
157-
*/
158-
protected static function saveConfigParams(array $data, $extra = null)
159-
{
160-
Logger::log('Saving required config parameters');
161-
// Store newly provided configuration parameters.
162-
if (!empty($extra) && array_key_exists('label', $extra) && is_array($extra['label'])) {
163-
foreach ($extra['label'] as $index => $field) {
164-
if (array_key_exists($index, $extra['value']) && !empty($extra['value'][$index])) {
165-
$data[$field] = $extra['value'][$index];
166-
}
167-
}
168-
}
169-
return $data;
170-
}
171-
172-
/**
173-
* @param array $data
174-
* @return array
175-
*/
176-
protected static function saveExtraParams(array $data)
177-
{
178-
$finalData = array();
179-
if (!empty($data)) {
180-
Logger::log('Saving extra configuration parameters');
181-
foreach (self::iterateConfigEntries($data) as [$key, $value]) {
182-
if (null !== $value) {
183-
$finalData[$key] = $value;
184-
}
185-
}
186-
}
187-
return $finalData;
188-
}
189155

190156
/**
191157
* @return boolean
@@ -324,20 +290,6 @@ public static function clearConfigFiles(): bool
324290
return $done;
325291
}
326292

327-
private static function shouldPersistConfigEntry(mixed $value, string $key): bool
328-
{
329-
if (in_array($key, self::$required, true)) {
330-
return true;
331-
}
332-
333-
// Keep explicit false/0 flags (security toggles rely on them).
334-
if (is_bool($value) || is_int($value) || is_float($value)) {
335-
return true;
336-
}
337-
338-
return $value !== null && $value !== '';
339-
}
340-
341293
protected function createRepository(): ConfigRepositoryInterface
342294
{
343295
$configPath = CONFIG_DIR . DIRECTORY_SEPARATOR . self::CONFIG_FILE;
@@ -352,14 +304,4 @@ protected function createRepository(): ConfigRepositoryInterface
352304
return $fileRepository;
353305
}
354306

355-
/**
356-
* @param array $data
357-
* @return \Generator
358-
*/
359-
private static function iterateConfigEntries(array $data): \Generator
360-
{
361-
foreach ($data as $key => $value) {
362-
yield [$key, $value];
363-
}
364-
}
365307
}

src/base/types/Api.php

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PSFS\base\types\helpers\attributes\HttpMethod;
1515
use PSFS\base\types\helpers\attributes\Label;
1616
use PSFS\base\types\helpers\attributes\Route as RouteAttribute;
17+
use PSFS\base\types\traits\Api\ApiCrudResponseTrait;
1718
use PSFS\base\types\traits\Api\Crud\ApiListTrait;
1819
use PSFS\base\types\traits\Api\ManagerTrait;
1920

@@ -22,7 +23,7 @@
2223
*/
2324
abstract class Api extends Singleton
2425
{
25-
use ManagerTrait, ApiListTrait;
26+
use ManagerTrait, ApiListTrait, ApiCrudResponseTrait;
2627

2728
const API_LIST_NAME_FIELD = '__name__';
2829
const API_FIELDS_RESULT_FIELD = '__fields';
@@ -69,27 +70,15 @@ public function init()
6970

7071
private function checkActions($method)
7172
{
72-
switch ($method) {
73-
default:
74-
case 'modelList':
75-
$this->action = self::API_ACTION_LIST;
76-
break;
77-
case 'get':
78-
$this->action = self::API_ACTION_GET;
79-
break;
80-
case 'post':
81-
$this->action = self::API_ACTION_POST;
82-
break;
83-
case 'put':
84-
$this->action = self::API_ACTION_PUT;
85-
break;
86-
case 'delete':
87-
$this->action = self::API_ACTION_DELETE;
88-
break;
89-
case 'bulk':
90-
$this->action = self::API_ACTION_BULK;
91-
break;
92-
}
73+
$actionMap = [
74+
'modelList' => self::API_ACTION_LIST,
75+
'get' => self::API_ACTION_GET,
76+
'post' => self::API_ACTION_POST,
77+
'put' => self::API_ACTION_PUT,
78+
'delete' => self::API_ACTION_DELETE,
79+
'bulk' => self::API_ACTION_BULK,
80+
];
81+
$this->action = $actionMap[(string)$method] ?? self::API_ACTION_LIST;
9382
}
9483

9584
/**
@@ -175,16 +164,12 @@ public function post()
175164
$message = t('Selected model could not be saved');
176165
}
177166
} catch (\Exception $e) {
178-
if (Config::getParam('debug')) {
179-
$message = t('An error occurred while saving the item: ') . '<br>' . $e->getMessage();
180-
} else {
181-
$message = t('An error occurred while saving the item: ') . '<br>' . $e->getCode();
182-
}
183-
$context = [];
184-
if (null !== $e->getPrevious()) {
185-
$context[] = $e->getPrevious()->getMessage();
186-
}
187-
Logger::log($e->getMessage(), LOG_CRIT, $context);
167+
$message = $this->buildMutationErrorMessage(
168+
'An error occurred while saving the item: ',
169+
$e,
170+
(bool)Config::getParam('debug')
171+
);
172+
$this->logCriticalException($e);
188173
}
189174

190175
return $this->json(new JsonResponse($model, $saved, $saved ? 1 : 0, 0, $message), $status);
@@ -224,18 +209,12 @@ public function put($pk)
224209
$message = t('An error occurred while updating the item, please check logs');
225210
}
226211
} catch (\Exception $e) {
227-
if (Config::getParam('debug')) {
228-
$message = t('An error occurred while updating the item: ') . '<br>' . $e->getMessage();
229-
} else {
230-
$message = t(
231-
'An error occurred while updating the item, please check logs: '
232-
) . '<br>' . $e->getCode();
233-
}
234-
$context = [];
235-
if (null !== $e->getPrevious()) {
236-
$context[] = $e->getPrevious()->getMessage();
237-
}
238-
Logger::log($e->getMessage(), LOG_CRIT, $context);
212+
$message = $this->buildMutationErrorMessage(
213+
'An error occurred while updating the item, please check logs: ',
214+
$e,
215+
(bool)Config::getParam('debug')
216+
);
217+
$this->logCriticalException($e);
239218
}
240219
} else {
241220
$message = t('Referenced model for update was not found');
@@ -275,11 +254,7 @@ public function delete($pk = null)
275254
$deleted = true;
276255
}
277256
} catch (\Exception $e) {
278-
$context = [];
279-
if (null !== $e->getPrevious()) {
280-
$context[] = $e->getPrevious()->getMessage();
281-
}
282-
Logger::log($e->getMessage(), LOG_CRIT, $context);
257+
$this->logCriticalException($e);
283258
}
284259
}
285260

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace PSFS\base\types\helpers;
4+
5+
class MetadataDocParser
6+
{
7+
public static function readTagValue(string $tag, string $doc, mixed $default = null): mixed
8+
{
9+
preg_match('/@' . preg_quote($tag, '/') . '\ (.*)(\n|\r)/im', $doc, $matches);
10+
return !empty($matches) ? $matches[1] : $default;
11+
}
12+
13+
public static function readHttpMethod(string $doc, mixed $default = null): string
14+
{
15+
preg_match('/@(GET|POST|PUT|DELETE|HEAD|PATCH)(\n|\r)/i', $doc, $routeMethod);
16+
return !empty($routeMethod) ? strtoupper($routeMethod[1]) : ($default ?? 'ALL');
17+
}
18+
19+
public static function readVisibilityFlag(string $doc): bool
20+
{
21+
$value = (string)self::readTagValue('visible', $doc, '');
22+
return !str_contains($value, 'false');
23+
}
24+
25+
public static function readVarType(string $doc): ?string
26+
{
27+
$type = null;
28+
if (preg_match(InjectorHelper::VAR_PATTERN, $doc, $matches) === 1) {
29+
list(, $type) = $matches;
30+
}
31+
return is_string($type) ? $type : null;
32+
}
33+
}
34+

src/base/types/helpers/MetadataReader.php

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -158,26 +158,16 @@ private static function readFromDoc(string $tag, ?string $doc, mixed $default =
158158
return $default;
159159
}
160160
return match ($tag) {
161-
'http' => self::readHttpMethodFromDoc($doc, $default),
162-
'visible' => self::readVisibleFromDoc($doc),
163-
'cache' => (bool)self::readFromDocValue('cache', $doc, $default ?? false),
164-
default => self::readFromDocValue($tag, $doc, $default),
161+
'http' => MetadataDocParser::readHttpMethod($doc, $default),
162+
'visible' => MetadataDocParser::readVisibilityFlag($doc),
163+
'cache' => (bool)MetadataDocParser::readTagValue('cache', $doc, $default ?? false),
164+
default => MetadataDocParser::readTagValue($tag, $doc, $default),
165165
};
166166
}
167167

168-
private static function readFromDocValue(string $needle, string $doc, mixed $default): mixed
169-
{
170-
preg_match('/@' . preg_quote($needle, '/') . '\ (.*)(\n|\r)/im', $doc, $matches);
171-
return !empty($matches) ? $matches[1] : $default;
172-
}
173-
174168
private static function readVarTypeFromDoc(string $doc): ?string
175169
{
176-
$type = null;
177-
if (preg_match(InjectorHelper::VAR_PATTERN, $doc, $matches) === 1) {
178-
list(, $type) = $matches;
179-
}
180-
return $type;
170+
return MetadataDocParser::readVarType($doc);
181171
}
182172

183173
private static function extractPropertyType(?ReflectionType $type): ?string
@@ -201,16 +191,4 @@ private static function logLegacyFallback(string $context): void
201191
Logger::log('[LegacyMetadata] ' . $context, LOG_NOTICE);
202192
}
203193

204-
private static function readHttpMethodFromDoc(string $doc, mixed $default = null): string
205-
{
206-
preg_match('/@(GET|POST|PUT|DELETE|HEAD|PATCH)(\n|\r)/i', $doc, $routeMethod);
207-
return !empty($routeMethod) ? strtoupper($routeMethod[1]) : ($default ?? 'ALL');
208-
}
209-
210-
private static function readVisibleFromDoc(string $doc): bool
211-
{
212-
preg_match('/@visible\ (.*)(\n|\r)/im', $doc, $matches);
213-
$value = !empty($matches) ? $matches[1] : '';
214-
return !str_contains((string)$value, 'false');
215-
}
216194
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace PSFS\base\types\traits\Api;
4+
5+
use Exception;
6+
use PSFS\base\Logger;
7+
8+
trait ApiCrudResponseTrait
9+
{
10+
protected function buildMutationErrorMessage(string $publicPrefix, Exception $e, bool $debug): string
11+
{
12+
if ($debug) {
13+
return t($publicPrefix) . '<br>' . $e->getMessage();
14+
}
15+
return t($publicPrefix) . '<br>' . $e->getCode();
16+
}
17+
18+
protected function logCriticalException(Exception $e): void
19+
{
20+
$context = $this->extractExceptionContext($e);
21+
Logger::log($e->getMessage(), LOG_CRIT, $context);
22+
}
23+
24+
/**
25+
* @return array<int, string>
26+
*/
27+
protected function extractExceptionContext(Exception $e): array
28+
{
29+
$context = [];
30+
if (null !== $e->getPrevious()) {
31+
$context[] = $e->getPrevious()->getMessage();
32+
}
33+
return $context;
34+
}
35+
}
36+

0 commit comments

Comments
 (0)