Compare commits
6 Commits
troys_issu
...
923dafecc9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
923dafecc9 | ||
|
|
cd031464e6 | ||
|
|
a093603c3c | ||
|
|
81c28b4a3b | ||
|
|
93ae2cdaa9 | ||
|
|
97c21a4894 |
99
NEXT_STEPS.md
Normal file
99
NEXT_STEPS.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Next Steps
|
||||
|
||||
- #TODO Unified error strategy (Definition)
|
||||
- Aufwand: `M`
|
||||
- Labels: `quality`, `api`
|
||||
- Ziel: Einheitliches Verhalten bei Fehlern.
|
||||
- Akzeptanzkriterien:
|
||||
- ADR/kurze Doku: wann `null/false`, wann Exception.
|
||||
- `sql.php`, `link-meta.php`, `troy-api.php` folgen derselben Strategie.
|
||||
- Mindestens 3 Beispiele in `README.md` dokumentiert.
|
||||
- Festlegung:
|
||||
- Exceptions fuer interne/unerwartete Fehler (Konfiguration fehlt, DB/HTTP/JSON-Fehler, Parsing-Fehler, invalide Argumente).
|
||||
- `null` nur fuer "kein Ergebnis" als erwarteter Zustand (z. B. URL ohne OG-Metadaten).
|
||||
- `false` nur fuer boolesche Checks/Operationen mit reinem Erfolg-Flag; keine Detailfehler ueber `false`.
|
||||
- Keine Mischung pro Funktion: jede Funktion dokumentiert exakt einen Fehlerkanal in PHPDoc/README.
|
||||
- Alle gecatchten Exceptions werden mit Kontext weitergeworfen (ohne Secrets), nicht still geschluckt.
|
||||
|
||||
- #TODO Complete `secret.php.example`
|
||||
- Aufwand: `S`
|
||||
- Labels: `docs`, `config`
|
||||
- Ziel: Vollstaendige Vorlagedatei fuer lokale Setups.
|
||||
- Akzeptanzkriterien:
|
||||
- Alle erwarteten Variablen aus `sql.php`, `mail.php`, `troy-api.php` enthalten.
|
||||
- Jede Variable hat kurzen Kommentar.
|
||||
- Dateiformat entspricht direkt nutzbarer Vorlage.
|
||||
|
||||
- #TODO Remove `@` error suppression incrementally
|
||||
- Aufwand: `M`
|
||||
- Labels: `quality`, `safety`
|
||||
- Ziel: Fehler sichtbar und kontrolliert behandeln.
|
||||
- Akzeptanzkriterien:
|
||||
- Alle `@`-Operatoren inventarisiert.
|
||||
- Ersetzungen mit explizitem Error-Handling umgesetzt.
|
||||
- Keine neue `@`-Verwendung in geaenderten Dateien.
|
||||
|
||||
- #TODO Sicherheit und Robustheit
|
||||
|
||||
- #TODO Harden URL fetching against SSRF
|
||||
- Aufwand: `M`
|
||||
- Labels: `security`, `network`
|
||||
- Akzeptanzkriterien:
|
||||
- Private/loopback ranges werden blockiert.
|
||||
- Optionales Host-Allowlist-Feature vorhanden.
|
||||
- Tests fuer geblockte und erlaubte Ziele vorhanden.
|
||||
|
||||
- #TODO Centralize HTTP limits (timeout/redirect/size)
|
||||
- Aufwand: `S`
|
||||
- Labels: `robustness`, `network`
|
||||
- Akzeptanzkriterien:
|
||||
- Eine zentrale Konfiguration fuer HTTP-Limits.
|
||||
- `og.php` und `link-meta.php` nutzen dieselben Limits.
|
||||
- Default-Werte sind in README dokumentiert.
|
||||
|
||||
- #TODO Improve SQL error handling + logging
|
||||
- Aufwand: `M`
|
||||
- Labels: `sql`, `robustness`
|
||||
- Akzeptanzkriterien:
|
||||
- `prepare()`/`execute()`-Fehler werden explizit behandelt.
|
||||
- Fehler enthalten Query-Kontext ohne Secrets.
|
||||
- Verhalten entspricht der definierten Error-Strategie.
|
||||
|
||||
- #TODO Replace fragile HTML allowlist sanitizer
|
||||
- Aufwand: `M`
|
||||
- Labels: `security`, `string`
|
||||
- Akzeptanzkriterien:
|
||||
- `onlySimpleHTML()` wird durch robusteren Ansatz ersetzt.
|
||||
- Erlaubte Tags sind konfigurierbar dokumentiert.
|
||||
- Regression-Tests fuer typische Eingaben vorhanden.
|
||||
|
||||
- #TODO Code-Qualitaet
|
||||
|
||||
- Sammel-Issue: Naming-Konvention, SQL-Binding-Refactor, Legacy-Markierung, Markdown-Konsolidierung, klare Modulgrenzen.
|
||||
- Aufwand: `L`
|
||||
- Empfehlung: in 3-5 Unter-Issues aufteilen.
|
||||
|
||||
- #TODO Tests und Tooling
|
||||
|
||||
- #TODO Bootstrap test/tooling baseline
|
||||
- Aufwand: `M`
|
||||
- Labels: `testing`, `ci`
|
||||
- Akzeptanzkriterien:
|
||||
- PHPUnit laeuft lokal mit ersten Smoke-Tests.
|
||||
- PHPStan/Psalm auf niedriger Stufe integriert.
|
||||
- CI fuehrt mindestens Lint + Tests bei Push aus.
|
||||
|
||||
- #TODO Prepare Composer + namespace migration path
|
||||
- Aufwand: `L`
|
||||
- Labels: `architecture`
|
||||
- Akzeptanzkriterien:
|
||||
- Vorschlag fuer Zielstruktur (`src/`, namespaces, autoload).
|
||||
- Migrationsplan fuer prozedurale Helfer zu Klassen.
|
||||
- Konfigurationsobjekt und HTTP-Adapter als Zielbild beschrieben.
|
||||
|
||||
## Empfohlene Reihenfolge
|
||||
|
||||
1. `#1` bis `#5` (kurzfristig, hoher Hebel)
|
||||
2. `#6` bis `#10` (Sicherheit/Robustheit)
|
||||
3. `#11` (Tests + CI als Guardrail)
|
||||
4. `#12` und Sammel-Issue aus Abschnitt 3
|
||||
185
README.md
185
README.md
@@ -1,2 +1,187 @@
|
||||
# php-func-lib
|
||||
|
||||
Kleine PHP-Utility-Bibliothek mit wiederverwendbaren Helfern fuer:
|
||||
|
||||
- Strings und einfache Sanitization
|
||||
- Zahlenformatierung
|
||||
- SQL-Zugriffe (mysqli + prepared statements)
|
||||
- Mailversand
|
||||
- Link/OpenGraph-Metadaten
|
||||
- Debug-Helfer
|
||||
- Troy-/Gitea-API-Aufrufe
|
||||
|
||||
## Installation
|
||||
|
||||
Als Git-Submodule in ein Projekt einbinden:
|
||||
|
||||
```bash
|
||||
git submodule add https://git.seemsleg.it/pub/php-func-lib lib
|
||||
```
|
||||
|
||||
Danach je nach Bedarf einzelne Dateien einbinden oder zentral ueber `_func.php` laden.
|
||||
|
||||
## Schnellstart
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/_func.php';
|
||||
|
||||
echo shortener("Ein sehr langer Text", 10); // "Ein sehr..."
|
||||
echo decade(12345); // "12.345 K" (je nach PHP-Konvertierung)
|
||||
```
|
||||
|
||||
## Module
|
||||
|
||||
- `string.php`: String-Helfer (`shortener`, `onlyAlpha`, `startsWith`, `endsWith`, `linkify`, ...)
|
||||
- `numbers.php`: Zahlen-Helfer (`decade`, `onlyNumeric`)
|
||||
- `sql.php`: Klasse `SQL` fuer Datenbankzugriffe (`get`, `single`, `list`, `keyval`, `set`)
|
||||
- `mail.php`: Mailfunktionen (`send_mail`, `send_html_mail`, `send_php_mail`)
|
||||
- `link-meta.php`: URL-Validierung, Fetching, Meta-Parsing, Bilddownload, Tag-Sanitization
|
||||
- `og.php`: Einfacher OG-Scan (`scanOG`)
|
||||
- `troy-api.php`: API-Helfer fuer Troy/Gitea (`sendToTroy`, `sendToGitea`)
|
||||
- `debug.php`: Cookie-basierte Debug-Ausgabe
|
||||
- `markdown.php`: einfache Markdown-nahe Formatierung (`md`)
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Einige Module erwarten ein lokales `secret.php` (siehe `secret.php.example`).
|
||||
Folgende Felder werden verwendet:
|
||||
|
||||
- `$_m['host']`, `$_m['user']`, `$_m['pass']`, `$_m['data']`, `$_m['pre']`, `$_m['salt']` fuer `sql.php`
|
||||
- `$_sendermail`, optional `$_smtp['srv']`, `$_smtp['user']`, `$_smtp['pw']` fuer `mail.php`
|
||||
- `$giteaUrl`, `$giteaOwner`, `$giteaRepo`, `$giteaToken` fuer `troy-api.php`
|
||||
|
||||
Beispiel:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// secret.php im selben Verzeichnis wie die Bibliothek ablegen
|
||||
if (!defined('SQL_LOG')) define('SQL_LOG', 0);
|
||||
$giteaUrl = 'https://git.example.org';
|
||||
$giteaOwner = 'org';
|
||||
$giteaRepo = 'repo';
|
||||
$giteaToken = 'token';
|
||||
```
|
||||
|
||||
## Runnable Examples
|
||||
|
||||
### `string.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/string.php';
|
||||
|
||||
echo shortener('Ein sehr langer Text', 12) . PHP_EOL;
|
||||
echo onlyAlpha('Hi! #42?') . PHP_EOL;
|
||||
echo linkify('Mehr Infos: https://example.org') . PHP_EOL;
|
||||
```
|
||||
|
||||
### `numbers.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/numbers.php';
|
||||
|
||||
echo decade(15320) . PHP_EOL;
|
||||
echo onlyNumeric('EUR -12.50') . PHP_EOL;
|
||||
```
|
||||
|
||||
### `sql.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
if (!defined('SQL_LOG')) define('SQL_LOG', 0);
|
||||
include_once __DIR__ . '/sql.php';
|
||||
|
||||
$sql = new SQL();
|
||||
$row = $sql->single('SELECT 1 AS ok');
|
||||
var_export($row);
|
||||
```
|
||||
|
||||
### `mail.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/mail.php';
|
||||
|
||||
send_mail('user@example.org', 'Test', 'Hallo Welt', 'ok', 'error');
|
||||
```
|
||||
|
||||
### `link-meta.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/string.php';
|
||||
include_once __DIR__ . '/link-meta.php';
|
||||
|
||||
$info = getPageInfo('https://example.org');
|
||||
if ($info['ok']) {
|
||||
echo $info['title'] . PHP_EOL;
|
||||
echo $info['description'] . PHP_EOL;
|
||||
}
|
||||
```
|
||||
|
||||
### `og.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/og.php';
|
||||
|
||||
$og = scanOG('https://example.org');
|
||||
print_r($og);
|
||||
```
|
||||
|
||||
### `troy-api.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/troy-api.php';
|
||||
|
||||
$res = sendToTroy(['msg' => 'hello']);
|
||||
var_dump($res);
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/troy-api.php';
|
||||
|
||||
try {
|
||||
$issue = sendToGitea('Test issue', 'Automatisch erstellt.');
|
||||
print_r($issue);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
```
|
||||
|
||||
### `debug.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/debug.php';
|
||||
|
||||
debugCookie(true);
|
||||
debug(['foo' => 'bar']);
|
||||
```
|
||||
|
||||
### `markdown.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
include_once __DIR__ . '/string.php';
|
||||
include_once __DIR__ . '/markdown.php';
|
||||
|
||||
echo md("! Titel\n\n* Punkt A\n* Punkt B");
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Kein Composer/Autoload; Includes muessen manuell gesetzt werden.
|
||||
- `sql.php` erwartet `secret.php` im Bibliotheksverzeichnis und nutzt `mysqli`.
|
||||
- Netzwerkfunktionen (`link-meta.php`, `og.php`, `troy-api.php`) nutzen `file_get_contents` und haben keine SSRF-Allowlist.
|
||||
- Mehrere Funktionen sind historisch gewachsen und nutzen teils inkonsistentes Error-Handling (`false`, `null`, Exceptions).
|
||||
- `markdown.php` und `onlySimpleHTML()` sind einfache Parser/Sanitizer, nicht vollstaendige Markdown- oder HTML-Sicherheitsloesungen.
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Die Bibliothek ist bewusst leichtgewichtig und ohne Composer-Setup gehalten.
|
||||
- Fuer geplante Verbesserungen siehe `NEXT_STEPS.md`.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
// include ('config.php');
|
||||
include_once ('sql.php');
|
||||
$sql = new SQL ();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
function debug($s) {
|
||||
declare(strict_types=1);
|
||||
|
||||
function debug(mixed $s): void {
|
||||
if(isset($_COOKIE['debug']))
|
||||
print_r($s);
|
||||
}
|
||||
|
||||
function debugCookie($on=true) {
|
||||
function debugCookie(bool $on = true): void {
|
||||
if($on) {
|
||||
setcookie('debug','1',time()+(60*60*24*365),"/");
|
||||
}else{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
$_ips_crawler = array (
|
||||
'34.79.234.76', // google
|
||||
'40.77.167.', // bing bot
|
||||
@@ -28,7 +30,7 @@ $_ips_crawler = array (
|
||||
'2a01:4f8:190:4244::2', // mj12bot
|
||||
'2a01:4f8:162:43c5::2', // mj12bot
|
||||
);
|
||||
function checkHuman() {
|
||||
function checkHuman(): bool {
|
||||
global $_ips_crawler;
|
||||
if (stripos ( $_SERVER ['HTTP_USER_AGENT'], 'bot' ) !== false || stripos ( $_SERVER ['HTTP_USER_AGENT'], 'crawler' ) !== false) {
|
||||
return false;
|
||||
|
||||
180
link-meta.php
Normal file
180
link-meta.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
function httpContext(int $timeout = 8) {
|
||||
return stream_context_create([
|
||||
'http' => [
|
||||
'timeout' => $timeout,
|
||||
'follow_location' => 1,
|
||||
'max_redirects' => 4,
|
||||
'user_agent' => 'star-citizen.de-linkbot/1.0',
|
||||
'ignore_errors' => true
|
||||
],
|
||||
'ssl' => [
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
function normalizeUrl(string $url): ?string {
|
||||
$url = trim($url);
|
||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = parse_url($url);
|
||||
if (!$parts || !isset($parts['scheme'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scheme = strtolower($parts['scheme']);
|
||||
if ($scheme !== 'http' && $scheme !== 'https') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
function resolveUrl(string $url, string $baseUrl): ?string {
|
||||
$url = trim($url);
|
||||
if (filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$baseParts = parse_url($baseUrl);
|
||||
if (!$baseParts || !isset($baseParts['scheme']) || !isset($baseParts['host'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strpos($url, '//') === 0) {
|
||||
return $baseParts['scheme'] . ':' . $url;
|
||||
}
|
||||
|
||||
$path = '/';
|
||||
if (!empty($baseParts['path'])) {
|
||||
$path = preg_replace('#/[^/]*$#', '/', $baseParts['path']);
|
||||
if ($path === null || $path === '') {
|
||||
$path = '/';
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($url) && $url[0] === '/') {
|
||||
return $baseParts['scheme'] . '://' . $baseParts['host'] . $url;
|
||||
}
|
||||
|
||||
return $baseParts['scheme'] . '://' . $baseParts['host'] . $path . $url;
|
||||
}
|
||||
|
||||
function safeFetch(string $url, int $timeout = 8): ?string {
|
||||
$ctx = httpContext($timeout);
|
||||
$content = @file_get_contents($url, false, $ctx);
|
||||
return $content === false ? null : $content;
|
||||
}
|
||||
|
||||
function downloadImageFromUrl(string $url, string $baseUrl, string $destinationFolder = 'upl/'): ?string {
|
||||
$resolved = resolveUrl($url, $baseUrl);
|
||||
if ($resolved === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!preg_match('/\.(jpg|jpeg|png|gif|bmp|webp)(?:\?|#|$)/i', $resolved)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$imageContent = safeFetch($resolved, 10);
|
||||
if ($imageContent === null || strlen($imageContent) === 0 || strlen($imageContent) > (5 * 1024 * 1024)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = parse_url($resolved, PHP_URL_PATH) ?? '';
|
||||
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
if ($ext === '' || !preg_match('/^(jpg|jpeg|png|gif|bmp|webp)$/', $ext)) {
|
||||
$ext = 'png';
|
||||
}
|
||||
|
||||
if (!is_dir($destinationFolder)) {
|
||||
@mkdir($destinationFolder, 0775, true);
|
||||
}
|
||||
|
||||
$md5Hash = md5($imageContent);
|
||||
$filePath = rtrim($destinationFolder, '/\\') . '/' . $md5Hash . '.' . $ext;
|
||||
$written = @file_put_contents($filePath, $imageContent);
|
||||
if ($written === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
function parseMetaContent(string $html, string $attr, string $name): ?string {
|
||||
$pattern = '/<meta[^>]*' . $attr . '=["\']' . preg_quote($name, '/') . '["\'][^>]*content=["\']([^"\']+)["\'][^>]*>/i';
|
||||
if (preg_match($pattern, $html, $matches) && isset($matches[1])) {
|
||||
return trim(html_entity_decode($matches[1], ENT_QUOTES | ENT_HTML5, 'UTF-8'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPageInfo(string $url): array {
|
||||
$ret = [
|
||||
'ok' => false,
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'logo' => null,
|
||||
'error' => null
|
||||
];
|
||||
|
||||
$normalized = normalizeUrl($url);
|
||||
if ($normalized === null) {
|
||||
$ret['error'] = 'ungueltige_url';
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$html = safeFetch($normalized, 10);
|
||||
if ($html === null) {
|
||||
$ret['error'] = 'seite_nicht_erreichbar';
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$title = parseMetaContent($html, 'property', 'og:title') ?? parseMetaContent($html, 'name', 'title');
|
||||
$description = parseMetaContent($html, 'property', 'og:description') ?? parseMetaContent($html, 'name', 'description');
|
||||
$image = parseMetaContent($html, 'property', 'og:image') ?? parseMetaContent($html, 'name', 'image');
|
||||
|
||||
if ($title === null && preg_match('/<title>\s*(.*?)\s*<\/title>/is', $html, $matchTitle)) {
|
||||
$title = trim(html_entity_decode($matchTitle[1], ENT_QUOTES | ENT_HTML5, 'UTF-8'));
|
||||
}
|
||||
|
||||
$logo = null;
|
||||
if ($image !== null && $image !== '') {
|
||||
$img = downloadImageFromUrl($image, $normalized);
|
||||
if ($img !== null) {
|
||||
$logo = '/' . ltrim($img, '/');
|
||||
}
|
||||
}
|
||||
|
||||
$ret['ok'] = true;
|
||||
$ret['title'] = $title ?? '';
|
||||
$ret['description'] = $description ?? '';
|
||||
$ret['logo'] = $logo;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function sanitizeTags(array $input): array {
|
||||
$ret = [];
|
||||
foreach ($input as $tag) {
|
||||
if (!is_string($tag)) {
|
||||
continue;
|
||||
}
|
||||
$clean = onlyAlpha(trim($tag), '_\-');
|
||||
if ($clean === '') {
|
||||
continue;
|
||||
}
|
||||
$clean = ucfirst(substr($clean, 0, 35));
|
||||
$ret[$clean] = true;
|
||||
if (count($ret) >= 20) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array_keys($ret);
|
||||
}
|
||||
|
||||
31
mail.php
31
mail.php
@@ -1,5 +1,18 @@
|
||||
<?php
|
||||
function send_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
declare(strict_types=1);
|
||||
|
||||
function mail_contains_header_injection(string $value): bool {
|
||||
return strpbrk($value, "\r\n\0") !== false;
|
||||
}
|
||||
|
||||
function mail_is_valid_email(string $value): bool {
|
||||
if (mail_contains_header_injection($value)) {
|
||||
return false;
|
||||
}
|
||||
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
|
||||
}
|
||||
|
||||
function send_mail(string $an, string $betreff, string $text, string $ok = '', string $error = ''): void {
|
||||
global $absender;
|
||||
$sender = 'noreply@troy-grunt.de';
|
||||
if(isset($absender) && $absender) {
|
||||
@@ -10,6 +23,10 @@ function send_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
$sender = $_sendermail;
|
||||
}
|
||||
}
|
||||
if (!mail_is_valid_email($an) || !mail_is_valid_email($sender) || mail_contains_header_injection($betreff)) {
|
||||
echo $error;
|
||||
return;
|
||||
}
|
||||
$header = 'From: ' . $sender . "\r\n";
|
||||
$header .= 'To: ' . $an . "\r\n";
|
||||
$header .= 'Content-Type:text/html' . "\r\n";
|
||||
@@ -23,7 +40,7 @@ function send_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
}
|
||||
}
|
||||
|
||||
function send_html_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
function send_html_mail(string $an, string $betreff, string $text, string $ok = '', string $error = ''): void {
|
||||
global $absender;
|
||||
$sender = 'noreply@troy-grunt.de';
|
||||
if(isset($absender) && $absender) {
|
||||
@@ -34,6 +51,10 @@ function send_html_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
$sender = $_sendermail;
|
||||
}
|
||||
}
|
||||
if (!mail_is_valid_email($an) || !mail_is_valid_email($sender) || mail_contains_header_injection($betreff)) {
|
||||
echo $error;
|
||||
return;
|
||||
}
|
||||
$boundary = md5($an.$betreff.$text.time());
|
||||
|
||||
$header = 'From: ' . $sender . "\n";
|
||||
@@ -57,12 +78,16 @@ function send_html_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
}
|
||||
}
|
||||
|
||||
function send_php_mail($an, $betreff, $text, $ok = '', $error = '') {
|
||||
function send_php_mail(string $an, string $betreff, string $text, string $ok = '', string $error = ''): void {
|
||||
global $_sendermail;
|
||||
$sender = 'noreply@troy-grunt.de';
|
||||
if (isset ( $_sendermail )) {
|
||||
$sender = $_sendermail;
|
||||
}
|
||||
if (!mail_is_valid_email($an) || !mail_is_valid_email($sender) || mail_contains_header_injection($betreff)) {
|
||||
echo $error;
|
||||
return;
|
||||
}
|
||||
include 'php-mailer/PHPMailer.php';
|
||||
$mail = new PHPMailer();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
// TODO markdown imple
|
||||
function md($str) {
|
||||
function md(string $str): string {
|
||||
// return nl2br ( $str ); // TODO md problem
|
||||
$text = '<p>';
|
||||
$lv = 0;
|
||||
@@ -75,7 +76,7 @@ function md($str) {
|
||||
$text .= '</p>';
|
||||
return $text;
|
||||
}
|
||||
function _md_link_replacer($in) {
|
||||
function _md_link_replacer(array $in): string {
|
||||
// var_dump ( $in );
|
||||
$in = explode ( '|', $in [1], 2 );
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
function decade($zahl)
|
||||
declare(strict_types=1);
|
||||
|
||||
function decade(int|float|string $zahl): int|float|string
|
||||
{
|
||||
if (! is_numeric($zahl) || $zahl == 0)
|
||||
return $zahl;
|
||||
@@ -31,7 +33,7 @@ function decade($zahl)
|
||||
return $zahl . ' ' . $si[$e];
|
||||
}
|
||||
|
||||
function onlyNumeric($num) {
|
||||
function onlyNumeric(string $num): string {
|
||||
return preg_replace("/[^0-9\.\-]+/", "", $num);
|
||||
}
|
||||
?>
|
||||
4
og.php
4
og.php
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
function scanOG($url) {
|
||||
declare(strict_types=1);
|
||||
|
||||
function scanOG(string $url): array {
|
||||
$og = array();
|
||||
$html = file_get_contents($url);
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
if (!defined('SQL_LOG')) define ( 'SQL_LOG', 1 ); // schreibt sql querys in eine log
|
||||
|
||||
$_m['host'] = 'localhost';
|
||||
@@ -8,8 +10,6 @@ $_m['data'] = '';
|
||||
$_m['pre'] = '';
|
||||
$_m['salt'] = '';
|
||||
|
||||
$_m['issuedata'] = ['domain'=>'','secret'=>''];
|
||||
|
||||
$_sendermail = 'noreply@.de';
|
||||
$_smtp['srv'] = 'mail.seemsleg.it';
|
||||
$_smtp['user'] = 'noreply@.de';
|
||||
|
||||
16
sql.php
16
sql.php
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
class SQL {
|
||||
private $h;
|
||||
private $res = false;
|
||||
private $m;
|
||||
public $salt;
|
||||
public $issuedata;
|
||||
public $pre;
|
||||
public $cnt_get = 0;
|
||||
public $cnt_set = 0;
|
||||
@@ -12,7 +13,6 @@ class SQL {
|
||||
require_once ('secret.php');
|
||||
|
||||
$this->m = $_m;
|
||||
$this->issuedata = $_m['issuedata'];
|
||||
$this->pre = $_m ['pre'];
|
||||
$this->salt = $_m ['salt'];
|
||||
if (SQL_LOG)
|
||||
@@ -24,7 +24,7 @@ class SQL {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public function get($que, $t = '', $p = array ()) {
|
||||
public function get(string $que, string $t = '', mixed $p = array ()): array|false {
|
||||
// echo $que;
|
||||
$this->cnt_get ++;
|
||||
if (SQL_LOG)
|
||||
@@ -91,14 +91,14 @@ class SQL {
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
public function single($que, $t = '', $p = array ()) {
|
||||
public function single(string $que, string $t = '', mixed $p = array ()): array|false {
|
||||
$data = $this->get ( $que, $t, $p );
|
||||
if ($data) {
|
||||
return $data [0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public function list($que, $t = '', $p = array ()) {
|
||||
public function list(string $que, string $t = '', mixed $p = array ()): array|false {
|
||||
$data = $this->get ( $que, $t, $p );
|
||||
if ($data) {
|
||||
$ret = array ();
|
||||
@@ -111,7 +111,7 @@ class SQL {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public function keyval($que, $k, $v, $t = '', $p = array ()) {
|
||||
public function keyval(string $que, string|int $k, string|int $v, string $t = '', mixed $p = array ()): array|false {
|
||||
$data = $this->get ( $que, $t, $p );
|
||||
if ($data) {
|
||||
$ret = array ();
|
||||
@@ -122,7 +122,7 @@ class SQL {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public function set($que, $t = '', $p = array (), $id = false) {
|
||||
public function set(string $que, string $t = '', mixed $p = array (), bool $id = false): int|false {
|
||||
// echo $que;
|
||||
$this->cnt_set ++;
|
||||
$statement = $this->h->prepare ( $que );
|
||||
@@ -191,7 +191,7 @@ class SQL {
|
||||
return $statement->affected_rows;
|
||||
}
|
||||
}
|
||||
function __destruct() {
|
||||
function __destruct(): void {
|
||||
if (SQL_LOG)
|
||||
$this->h->close ();
|
||||
// echo 'DESTROY';
|
||||
|
||||
28
string.php
28
string.php
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
function umlaute($str) {
|
||||
declare(strict_types=1);
|
||||
|
||||
function umlaute(string $str): string {
|
||||
return str_replace ( array (
|
||||
'Ä',
|
||||
'Ö',
|
||||
@@ -20,10 +22,10 @@ function umlaute($str) {
|
||||
'&'
|
||||
), $str );
|
||||
}
|
||||
function chk($str) {
|
||||
function chk(string $str): string {
|
||||
return str_replace ( "'", '"', $str );
|
||||
}
|
||||
function noScript($str) {
|
||||
function noScript(string $str): string {
|
||||
return str_replace ( array (
|
||||
'<',
|
||||
'>'
|
||||
@@ -32,7 +34,7 @@ function noScript($str) {
|
||||
'>'
|
||||
), $str );
|
||||
}
|
||||
function random($name_laenge) {
|
||||
function random(int $name_laenge): string {
|
||||
$zeichen = "abcedfghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTSUVWXYZ0123456789";
|
||||
$name_neu = "";
|
||||
|
||||
@@ -43,32 +45,32 @@ function random($name_laenge) {
|
||||
}
|
||||
return $name_neu;
|
||||
}
|
||||
function startsWith($haystack, $needle) {
|
||||
function startsWith(string $haystack, string $needle): bool {
|
||||
$length = strlen ( $needle );
|
||||
return (substr ( $haystack, 0, $length ) === $needle);
|
||||
}
|
||||
function endsWith($haystack, $needle) {
|
||||
function endsWith(string $haystack, string $needle): bool {
|
||||
$length = strlen ( $needle );
|
||||
|
||||
return $length === 0 || (substr ( $haystack, - $length ) === $needle);
|
||||
}
|
||||
function onlyAlpha($str, $zus = '') {
|
||||
function onlyAlpha(string $str, string $zus = ''): string {
|
||||
return preg_replace ( "/[^a-zA-Z0-9 \-\{$zus}_]+/", "", $str );
|
||||
}
|
||||
function shortener($str, $len = 50, $fill = '...') {
|
||||
function shortener(string $str, int $len = 50, string $fill = '...'): string {
|
||||
if (strlen ( $str ) > $len) {
|
||||
$str = substr ( $str, 0, $len - strlen ( $fill ) ) . $fill;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
function isEmail($str) {
|
||||
function isEmail(string $str): string|false {
|
||||
$match = preg_match ( "/[a-zA-Z0-9\-\_\.]*\@[a-zA-Z0-9\-\_\.]*.[a-z]{2,10}/", $str );
|
||||
if ($match) {
|
||||
return $str;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function markUp($text) {
|
||||
function markUp(string $text): string {
|
||||
$r = '';
|
||||
$lv = 0;
|
||||
foreach ( explode ( "\n", $text ) as $t ) {
|
||||
@@ -127,7 +129,7 @@ function markUp($text) {
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
function onlySimpleHTML($s) {
|
||||
function onlySimpleHTML(string $s): string {
|
||||
$s = str_replace ( array (
|
||||
'<',
|
||||
'>'
|
||||
@@ -259,11 +261,11 @@ function onlySimpleHTML($s) {
|
||||
|
||||
return $s;
|
||||
}
|
||||
function linkify($input) {
|
||||
function linkify(string $input): string {
|
||||
$pattern = '@(http(s)?://[a-zA-Z0-9/\.\#\-\_]*)@';
|
||||
return $output = preg_replace ( $pattern, '<a href="$1">$1</a>', $input );
|
||||
}
|
||||
function inStr($needle, $haystack) {
|
||||
function inStr(string $needle, string $haystack): bool {
|
||||
if (strpos ( $haystack, $needle ) !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
45
troy-api.php
45
troy-api.php
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
function sendToTroy($data) {
|
||||
declare(strict_types=1);
|
||||
|
||||
function sendToTroy(array $data): string|false {
|
||||
$url = 'https://troy-grunt.de/api.php';
|
||||
$options = array (
|
||||
'http' => array (
|
||||
@@ -14,37 +16,36 @@ function sendToTroy($data) {
|
||||
return file_get_contents ( $url, false, $context );
|
||||
}
|
||||
|
||||
function troysIssue($ident, $typ, $text = null, $data = [], $reaction = []) {
|
||||
global $sql;
|
||||
$url = "https://issues.troy-grunt.de/api/input";
|
||||
function sendToGitea(string $title, string $message): array|null {
|
||||
// secret.php liegt in lib/
|
||||
require 'secret.php';
|
||||
|
||||
$payload = [
|
||||
"domain" => $sql->issuedata['domain'],
|
||||
"secret" => $sql->issuedata['secret'],
|
||||
"ident" => $ident,
|
||||
"typ" => $typ,
|
||||
"text" => $text,
|
||||
"data" => $data,
|
||||
"reaction" => $reaction
|
||||
$url = rtrim($giteaUrl, '/') . "/repos/$giteaOwner/$giteaRepo/issues";
|
||||
|
||||
$data = [
|
||||
"title" => $title,
|
||||
"body" => $message
|
||||
];
|
||||
|
||||
// Entferne null-Werte aus dem Array
|
||||
$payload = array_filter($payload, function ($value) {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
$options = [
|
||||
"http" => [
|
||||
"header" => "Content-Type: application/json\r\n",
|
||||
"method" => "POST",
|
||||
"content" => json_encode($payload),
|
||||
"ignore_errors" => true
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'header' => [
|
||||
"Content-Type: application/json",
|
||||
"Authorization: token $giteaToken"
|
||||
],
|
||||
'content' => json_encode($data)
|
||||
]
|
||||
];
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($url, false, $context);
|
||||
|
||||
if ($result === FALSE) {
|
||||
throw new Exception("Fehler beim Erstellen der Anfrage");
|
||||
}
|
||||
|
||||
return json_decode($result, true);
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user