Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b31ce0d16 | |||
| 964bfa877c | |||
| 36fa3fdeae | |||
| 645037d144 | |||
| b8ab53a6f3 | |||
| dd31c062ad | |||
| 869f25d6db | |||
| b41fa58488 | |||
| 1b4c6fe66a | |||
| 320710fd02 | |||
| 11d720aa25 | |||
| 08bd6d23c9 | |||
| 28de4e88b7 | |||
| 0c1e916ed6 | |||
| 1bebdff3ac | |||
| 5e6c3e46fc | |||
| ff227fa6e0 | |||
| 2e715e803e | |||
| 8e6b29976c |
17
.mcp.json
Normal file
17
.mcp.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"serena": {
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"--from",
|
||||
"git+https://github.com/oraios/serena",
|
||||
"serena",
|
||||
"start-mcp-server",
|
||||
"--context",
|
||||
"ide-assistant",
|
||||
"--project",
|
||||
"C:/visual studio code/projekty/cmsPRO"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
1
.phpunit.result.cache
Normal file
1
.phpunit.result.cache
Normal file
@@ -0,0 +1 @@
|
||||
{"version":2,"defects":{"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsReturnsEmptyArrayWhenDbReturnsNull":8,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsUsesCache":8},"times":{"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguagesListReturnsArray":0.041,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguagesListReturnsEmptyWhenNull":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguageDetailsReturnsRowWhenFound":0,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguageDetailsReturnsNullWhenNotFound":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testActiveLanguagesQueriesDbAndCaches":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testActiveLanguagesReturnsEmptyWhenNull":0,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testMaxOrderReturnsInteger":0,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testTranslationDeleteReturnsTrueOnSuccess":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testTranslationDeleteReturnsFalseOnFailure":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testTranslationDetailsReturnsRowOrNull":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsReturnsMappedArray":0.001,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsReturnsEmptyArrayWhenDbReturnsNull":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsUsesCache":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testUpdateCallsDbUpdateWhenParamExists":0.001,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testUpdateCallsDbInsertWhenParamMissing":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testVisitCounterReturnsValue":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testVisitCounterReturnsNullWhenEmpty":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testFindReturnsUserArray":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testFindReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testFindByLoginReturnsUser":0.002,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testAllReturnsArray":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testAllReturnsEmptyArrayWhenNull":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testHasPrivilegeReturnsTrueForAdminUser":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testHasPrivilegeReturnsTrueWhenPrivilegeExists":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testHasPrivilegeReturnsFalseWhenPrivilegeMissing":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testLogonReturnsZeroWhenUserNotFound":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testLogonReturnsMinusOneWhenAccountBlocked":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testLogonReturnsOneOnSuccess":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testIsLoginTakenReturnsTrueWhenExists":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testIsLoginTakenReturnsFalseWhenFree":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsFalseWhenUserNotFound":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsFalseWhenTooManyFailedAttempts":0.075,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsFalseWhenExpired":0.073,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsTrueOnValidCode":0.148,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testDeleteReturnsTrueOnSuccess":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testSaveReturnsErrorWhenPasswordTooShort":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testSaveReturnsErrorWhenPasswordsMismatch":0}}
|
||||
@@ -48,6 +48,15 @@ backups/
|
||||
cache/
|
||||
cron/
|
||||
|
||||
# Moduł zarządzania releaseami (tylko serwer dewelopera)
|
||||
autoload/admin/controls/class.Releases.php
|
||||
autoload/admin/factory/class.Releases.php
|
||||
autoload/admin/view/class.Releases.php
|
||||
admin/templates/releases/
|
||||
|
||||
# Menu dewelopera
|
||||
admin/templates/additional-menu.php
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.serena/
|
||||
|
||||
7
.vscode/ftp-kr.json
vendored
7
.vscode/ftp-kr.json
vendored
@@ -12,6 +12,11 @@
|
||||
"ignoreRemoteModification": true,
|
||||
"ignore": [
|
||||
".git",
|
||||
"/.vscode"
|
||||
"/.vscode",
|
||||
"/.claude",
|
||||
"/.serena",
|
||||
"/docs",
|
||||
"AGENTS.md",
|
||||
"CLAUDE.md"
|
||||
]
|
||||
}
|
||||
41
AGENTS.md
Normal file
41
AGENTS.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Workflow
|
||||
|
||||
## Sposób pracy
|
||||
- Pisz do mnie po polsku, zwięźle i krótko, ale merytorycznie
|
||||
|
||||
## Zasady pisania kodu
|
||||
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
|
||||
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
|
||||
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 30–50 linii (jeśli dłuższe – dzielić)
|
||||
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
|
||||
- Nazewnictwo:
|
||||
- klasy: PascalCase
|
||||
- metody/zmienne: camelCase
|
||||
- stałe: UPPER_SNAKE_CASE
|
||||
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
|
||||
- medoo + prepared statements bez wyjątków (żadnego sklejania SQL stringiem)
|
||||
- XSS: escape w widokach (np. helper e())
|
||||
- CSRF dla formularzy, sensowna obsługa sesji
|
||||
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
|
||||
|
||||
|
||||
## Wprowadzanie zmian
|
||||
- Przeanalizuj wprowadzone zadanie
|
||||
- Jeżeli masz jakieś wątpliwości pytaj
|
||||
- Przedstaw plan
|
||||
- Po akceptacji wdróź plan
|
||||
|
||||
## KONIEC PRACY
|
||||
|
||||
Gdy użytkownik napisze `KONIEC PRACY`, wykonaj kolejno:
|
||||
|
||||
1. Przeprowadzenie testów.
|
||||
2. Aktualizacja dokumentacji technicznej, jeśli zmiany tego wymagają:
|
||||
- `docs/PROJECT_STRUCTURE.md`
|
||||
- `docs/FORM_EDIT_SYSTEM.md`
|
||||
3. Migracje SQL (jeśli były zmiany w bazie danych):
|
||||
- Plik: `migrations/{version}.sql` (np. `migrations/0.304.sql`)
|
||||
- **NIE** w `updates/` — build script sam wczyta z `migrations/`
|
||||
- Sprawdź czy plik istnieje i jest poprawnie nazwany przed commitem
|
||||
4. Commit.
|
||||
5. Push.
|
||||
11
admin/templates/additional-menu.php
Normal file
11
admin/templates/additional-menu.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
// Menu tylko na serwerze dewelopera — wykluczone z .updateignore
|
||||
?>
|
||||
<div class="title">Developer</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/releases/main_view/">
|
||||
<img src="/admin/css/icons/settings-20-filled.svg">Releases & Licencje
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
312
admin/templates/releases/main-view.php
Normal file
312
admin/templates/releases/main-view.php
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
global $gdb;
|
||||
ob_start();
|
||||
?>
|
||||
<style>
|
||||
.releases-tabs-nav { margin-bottom: 0; border-bottom: 1px solid #ddd; }
|
||||
.releases-tabs-nav li { display: inline-block; margin-bottom: -1px; }
|
||||
.releases-tabs-nav li a {
|
||||
display: block; padding: 8px 16px; text-decoration: none; color: #555;
|
||||
border: 1px solid transparent; border-radius: 3px 3px 0 0;
|
||||
background: #f5f5f5; margin-right: 2px; cursor: pointer;
|
||||
}
|
||||
.releases-tabs-nav li.active a {
|
||||
color: #333; background: #fff;
|
||||
border-color: #ddd #ddd #fff;
|
||||
}
|
||||
.releases-tab-pane { display: none; padding: 18px 0 0; }
|
||||
.releases-tab-pane.active { display: block; }
|
||||
.license-form-wrap { display: none; margin-bottom: 20px; }
|
||||
</style>
|
||||
|
||||
<ul class="releases-tabs-nav" id="releases-tabs-nav">
|
||||
<li class="active"><a href="#" data-tab="tab-versions">Wersje</a></li>
|
||||
<li><a href="#" data-tab="tab-licenses">Licencje</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- TAB: Wersje -->
|
||||
<div class="releases-tab-pane active" id="tab-versions">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<form method="post" action="/admin/releases/discover_versions/" style="display:inline"
|
||||
onsubmit="return confirm('Wykryć wersje z dysku i zarejestrować jako stable?')">
|
||||
<button type="submit" class="btn btn-info btn-sm">
|
||||
<i class="fa fa-search"></i> Wykryj wersje z dysku
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<table class="table table-bordered table-striped table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Wersja</th>
|
||||
<th class="text-center" style="width:100px;">Kanał</th>
|
||||
<th style="width:150px;">Data dodania</th>
|
||||
<th style="width:150px;">Data promocji</th>
|
||||
<th class="text-center" style="width:60px;">ZIP</th>
|
||||
<th class="text-center" style="width:140px;">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($this->versions)): ?>
|
||||
<tr><td colspan="6" class="text-center text-muted">Brak wersji w bazie.</td></tr>
|
||||
<?php else: foreach ($this->versions as $v): ?>
|
||||
<tr>
|
||||
<td><strong><?= htmlspecialchars($v['version']) ?></strong></td>
|
||||
<td class="text-center">
|
||||
<?php if ($v['channel'] === 'stable'): ?>
|
||||
<span class="label label-success">stable</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-warning">beta</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($v['created_at'] ?? '') ?></td>
|
||||
<td><?= $v['promoted_at'] ? htmlspecialchars($v['promoted_at']) : '<span class="text-muted">—</span>' ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($v['zip_exists']): ?>
|
||||
<span class="text-success"><i class="fa fa-check"></i></span>
|
||||
<?php else: ?>
|
||||
<span class="text-danger"><i class="fa fa-times"></i></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?php if ($v['channel'] === 'beta'): ?>
|
||||
<form method="post" action="/admin/releases/promote/" style="display:inline">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<button type="submit" class="btn btn-success btn-xs"
|
||||
onclick="return confirm('Promować <?= htmlspecialchars($v['version'], ENT_QUOTES) ?> do stable?')">
|
||||
Promuj →stable
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<form method="post" action="/admin/releases/demote/" style="display:inline">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<button type="submit" class="btn btn-warning btn-xs"
|
||||
onclick="return confirm('Cofnąć <?= htmlspecialchars($v['version'], ENT_QUOTES) ?> do beta?')">
|
||||
Cofnij →beta
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Licencje -->
|
||||
<div class="releases-tab-pane" id="tab-licenses">
|
||||
|
||||
<div style="margin-bottom: 12px;">
|
||||
<a href="#" class="btn btn-success btn-sm" id="btn-add-license">
|
||||
<i class="fa fa-plus-circle"></i> Dodaj licencję
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Formularz dodawania / edycji -->
|
||||
<div class="license-form-wrap panel panel-default" id="license-form-wrap">
|
||||
<div class="panel-heading">
|
||||
<strong id="license-form-title">Nowa licencja</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form method="post" action="/admin/releases/save_license/" id="license-form">
|
||||
<input type="hidden" name="id" id="lic-id" value="">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label>Domena</label>
|
||||
<input type="text" name="domain" id="lic-domain" class="form-control" placeholder="np. example.com" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label>Klucz licencji</label>
|
||||
<input type="text" name="key" id="lic-key" class="form-control" placeholder="Klucz UUID / losowy ciąg (pusty = domyślny)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label>Ważna do daty</label>
|
||||
<input type="date" name="valid_to_date" id="lic-valid-date" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label>Ważna do wersji</label>
|
||||
<input type="text" name="valid_to_version" id="lic-valid-ver" class="form-control" placeholder="np. 1.700">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label>Dostęp beta</label>
|
||||
<select name="beta" id="lic-beta" class="form-control">
|
||||
<option value="0">Nie</option>
|
||||
<option value="1">Tak</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Notatka</label>
|
||||
<input type="text" name="note" id="lic-note" class="form-control" placeholder="Opcjonalna notatka">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-system btn-sm">
|
||||
<i class="fa fa-save"></i> Zapisz licencję
|
||||
</button>
|
||||
<a href="#" class="btn btn-default btn-sm" id="btn-cancel-license">Anuluj</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabela licencji -->
|
||||
<table class="table table-bordered table-striped table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domena</th>
|
||||
<th style="width:120px;">Klucz</th>
|
||||
<th style="width:120px;">Do daty</th>
|
||||
<th style="width:100px;">Do wersji</th>
|
||||
<th class="text-center" style="width:70px;">Beta</th>
|
||||
<th>Notatka</th>
|
||||
<th class="text-center" style="width:60px;">Edytuj</th>
|
||||
<th class="text-center" style="width:60px;">Usuń</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($this->licenses)): ?>
|
||||
<tr><td colspan="8" class="text-center text-muted">Brak licencji w bazie.</td></tr>
|
||||
<?php else: foreach ($this->licenses as $lic): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($lic['domain']) ?></td>
|
||||
<td>
|
||||
<code title="<?= htmlspecialchars($lic['key']) ?>">
|
||||
<?= htmlspecialchars(substr($lic['key'], 0, 8)) ?>…
|
||||
</code>
|
||||
</td>
|
||||
<td><?= $lic['valid_to_date'] ? htmlspecialchars($lic['valid_to_date']) : '<span class="text-muted">—</span>' ?></td>
|
||||
<td><?= $lic['valid_to_version'] ? htmlspecialchars($lic['valid_to_version']) : '<span class="text-muted">—</span>' ?></td>
|
||||
<td class="text-center">
|
||||
<form method="post" action="/admin/releases/toggle_beta/" style="display:inline">
|
||||
<input type="hidden" name="id" value="<?= (int)$lic['id'] ?>">
|
||||
<button type="submit"
|
||||
class="label <?= $lic['beta'] ? 'label-info' : 'label-default' ?>"
|
||||
title="Kliknij, aby przełączyć"
|
||||
style="cursor:pointer;border:none;background:none">
|
||||
<?= $lic['beta'] ? 'tak' : 'nie' ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($lic['note'] ?? '') ?></td>
|
||||
<td class="text-center">
|
||||
<a href="#"
|
||||
class="btn btn-default btn-xs btn-edit-license"
|
||||
data-id="<?= (int)$lic['id'] ?>"
|
||||
data-domain="<?= htmlspecialchars($lic['domain'], ENT_QUOTES) ?>"
|
||||
data-key="<?= htmlspecialchars($lic['key'], ENT_QUOTES) ?>"
|
||||
data-valid-date="<?= htmlspecialchars($lic['valid_to_date'] ?? '', ENT_QUOTES) ?>"
|
||||
data-valid-ver="<?= htmlspecialchars($lic['valid_to_version'] ?? '', ENT_QUOTES) ?>"
|
||||
data-beta="<?= (int)$lic['beta'] ?>"
|
||||
data-note="<?= htmlspecialchars($lic['note'] ?? '', ENT_QUOTES) ?>">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<form method="post" action="/admin/releases/delete_license/" style="display:inline"
|
||||
onsubmit="return confirm('Usunąć licencję dla <?= htmlspecialchars($lic['domain'], ENT_QUOTES) ?>?')">
|
||||
<input type="hidden" name="id" value="<?= (int)$lic['id'] ?>">
|
||||
<button type="submit" class="btn btn-danger btn-xs">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// Tab switching
|
||||
document.querySelectorAll('#releases-tabs-nav a[data-tab]').forEach(function (link) {
|
||||
link.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
var targetId = this.getAttribute('data-tab');
|
||||
document.querySelectorAll('#releases-tabs-nav li').forEach(function (li) {
|
||||
li.classList.remove('active');
|
||||
});
|
||||
document.querySelectorAll('.releases-tab-pane').forEach(function (pane) {
|
||||
pane.classList.remove('active');
|
||||
});
|
||||
this.parentElement.classList.add('active');
|
||||
document.getElementById(targetId).classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Show add-license form
|
||||
document.getElementById('btn-add-license').addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
resetLicenseForm();
|
||||
document.getElementById('license-form-title').textContent = 'Nowa licencja';
|
||||
var wrap = document.getElementById('license-form-wrap');
|
||||
if (wrap.style.display === 'none' || wrap.style.display === '') {
|
||||
wrap.style.display = 'block';
|
||||
$(wrap).slideDown(200);
|
||||
}
|
||||
});
|
||||
|
||||
// Cancel
|
||||
document.getElementById('btn-cancel-license').addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
$(document.getElementById('license-form-wrap')).slideUp(200);
|
||||
});
|
||||
|
||||
// Edit buttons
|
||||
document.querySelectorAll('.btn-edit-license').forEach(function (btn) {
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
var d = this.dataset;
|
||||
document.getElementById('lic-id').value = d.id;
|
||||
document.getElementById('lic-domain').value = d.domain;
|
||||
document.getElementById('lic-key').value = d.key;
|
||||
document.getElementById('lic-valid-date').value = d.validDate;
|
||||
document.getElementById('lic-valid-ver').value = d.validVer;
|
||||
document.getElementById('lic-beta').value = d.beta;
|
||||
document.getElementById('lic-note').value = d.note;
|
||||
document.getElementById('license-form-title').textContent = 'Edytuj licencję: ' + d.domain;
|
||||
var wrap = document.getElementById('license-form-wrap');
|
||||
wrap.style.display = 'block';
|
||||
$(wrap).slideDown(200);
|
||||
wrap.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
});
|
||||
});
|
||||
|
||||
function resetLicenseForm() {
|
||||
document.getElementById('lic-id').value = '';
|
||||
document.getElementById('lic-domain').value = '';
|
||||
document.getElementById('lic-key').value = '';
|
||||
document.getElementById('lic-valid-date').value = '';
|
||||
document.getElementById('lic-valid-ver').value = '';
|
||||
document.getElementById('lic-beta').value = '0';
|
||||
document.getElementById('lic-note').value = '';
|
||||
}
|
||||
|
||||
// If URL hash indicates licenses tab, switch to it on load
|
||||
if (window.location.hash === '#licenses') {
|
||||
document.querySelector('[data-tab="tab-licenses"]').click();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
$out = ob_get_clean();
|
||||
|
||||
$grid = new \gridEdit;
|
||||
$grid->id = 'releases-view';
|
||||
$grid->gdb_opt = $gdb;
|
||||
$grid->include_plugins = true;
|
||||
$grid->title = 'Releases & Licencje';
|
||||
$grid->default_buttons = false;
|
||||
$grid->form = false;
|
||||
$grid->external_code = $out;
|
||||
echo $grid->draw();
|
||||
?>
|
||||
648
autoload/Domain/Articles/ArticlesRepository.php
Normal file
648
autoload/Domain/Articles/ArticlesRepository.php
Normal file
@@ -0,0 +1,648 @@
|
||||
<?php
|
||||
namespace Domain\Articles;
|
||||
|
||||
class ArticlesRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function filesOrderSave( $articleId, $order ): void
|
||||
{
|
||||
$i = 0;
|
||||
$order = explode( ';', $order );
|
||||
|
||||
if ( is_array( $order ) && !empty( $order ) )
|
||||
foreach ( $order as $fileId )
|
||||
$this->db->update( 'pp_articles_files', [ 'o' => (int)$i++ ], [
|
||||
'AND' => [ 'article_id' => $articleId, 'id' => $fileId ]
|
||||
] );
|
||||
}
|
||||
|
||||
public function galleryOrderSave( $articleId, $order ): void
|
||||
{
|
||||
$i = 0;
|
||||
$order = explode( ';', $order );
|
||||
|
||||
if ( is_array( $order ) && !empty( $order ) )
|
||||
foreach ( $order as $imageId )
|
||||
$this->db->update( 'pp_articles_images', [ 'o' => $i++ ], [
|
||||
'AND' => [ 'article_id' => $articleId, 'id' => $imageId ]
|
||||
] );
|
||||
}
|
||||
|
||||
public function additionalParams( $language = 0 )
|
||||
{
|
||||
return $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => $language ] ] );
|
||||
}
|
||||
|
||||
public function imageAltChange( $imageId, $imageAlt )
|
||||
{
|
||||
$result = $this->db->update( 'pp_articles_images', [ 'alt' => $imageAlt ], [ 'id' => $imageId ] );
|
||||
\S::delete_cache();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function articleUrl( $articleId )
|
||||
{
|
||||
$results = $this->db->query(
|
||||
"SELECT seo_link FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$articleId . " AND seo_link != '' ORDER BY o ASC LIMIT 1"
|
||||
)->fetchAll();
|
||||
|
||||
if ( !$results[0]['seo_link'] )
|
||||
{
|
||||
$title = $this->articleTitle( $articleId );
|
||||
return 'a-' . $articleId . '-' . \S::seo( $title );
|
||||
}
|
||||
|
||||
return $results[0]['seo_link'];
|
||||
}
|
||||
|
||||
public function articlePages( $articleId )
|
||||
{
|
||||
$pagesRepo = new \Domain\Pages\PagesRepository( $this->db );
|
||||
$results = $this->db->query( "SELECT page_id FROM pp_articles_pages WHERE article_id = " . (int)$articleId )->fetchAll();
|
||||
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( $out == '' )
|
||||
$out .= ' - ';
|
||||
|
||||
$out .= $pagesRepo->pageTitle( $row['page_id'] );
|
||||
|
||||
if ( end( $results ) != $row )
|
||||
$out .= ' / ';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function articleTitle( $articleId )
|
||||
{
|
||||
$results = $this->db->query(
|
||||
"SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$articleId . " AND title != '' ORDER BY o ASC LIMIT 1"
|
||||
)->fetchAll();
|
||||
|
||||
return $results[0]['title'];
|
||||
}
|
||||
|
||||
public function deleteFile( $fileId ): bool
|
||||
{
|
||||
$this->db->update( 'pp_articles_files', [ 'to_delete' => 1 ], [ 'id' => (int)$fileId ] );
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteImg( $imageId ): bool
|
||||
{
|
||||
$this->db->update( 'pp_articles_images', [ 'to_delete' => 1 ], [ 'id' => (int)$imageId ] );
|
||||
return true;
|
||||
}
|
||||
|
||||
public function articleDetails( $articleId )
|
||||
{
|
||||
if ( $article = $this->db->get( 'pp_articles', '*', [ 'id' => (int)$articleId ] ) )
|
||||
{
|
||||
$results = $this->db->select( 'pp_articles_langs', '*', [ 'article_id' => (int)$articleId ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$article['languages'][ $row['lang_id'] ] = $row;
|
||||
|
||||
$article['images'] = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => (int)$articleId, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||
$article['files'] = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => (int)$articleId, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||
$article['pages'] = $this->db->select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$articleId ] );
|
||||
$article['tags'] = $this->db->select( 'pp_tags', [ '[><]pp_articles_tags' => [ 'id' => 'tag_id' ] ], 'name', [ 'article_id' => (int)$articleId ] );
|
||||
$article['params'] = $this->db->select( 'pp_articles_additional_values', [ 'param_id', 'value', 'language_id' ], [ 'article_id' => (int)$articleId ] );
|
||||
}
|
||||
|
||||
return $article;
|
||||
}
|
||||
|
||||
public function insertMissingHash(): bool
|
||||
{
|
||||
if ( $this->db->count( 'pp_articles', [ 'hash' => null ] ) )
|
||||
{
|
||||
$rows = $this->db->select( 'pp_articles', [ 'id', 'date_add' ], [ 'hash' => null ] );
|
||||
if ( is_array( $rows ) )
|
||||
foreach ( $rows as $row )
|
||||
$this->db->update( 'pp_articles', [ 'hash' => md5( $row['id'] . $row['date_add'] ) ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function articlesByDateAdd( $dateStart, $dateEnd )
|
||||
{
|
||||
$results = $this->db->query(
|
||||
'SELECT id FROM pp_articles WHERE status = 1 AND date_add BETWEEN \'' . $dateStart . '\' AND \'' . $dateEnd . '\' ORDER BY date_add DESC'
|
||||
)->fetchAll();
|
||||
|
||||
if ( is_array( $results ) && !empty( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$articles[] = \front\factory\Articles::article_details( $row['id'], 'pl' );
|
||||
|
||||
return isset( $articles ) ? $articles : null;
|
||||
}
|
||||
|
||||
public function articlesSetArchive( $articleId )
|
||||
{
|
||||
$result = $this->db->update( 'pp_articles', [ 'status' => -1 ], [ 'id' => (int)$articleId ] );
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function fileNameChange( $fileId, $fileName ): bool
|
||||
{
|
||||
$this->db->update( 'pp_articles_files', [ 'name' => $fileName ], [ 'id' => (int)$fileId ] );
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteNonassignedFiles(): void
|
||||
{
|
||||
$results = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
|
||||
$this->db->delete( 'pp_articles_files', [ 'article_id' => null ] );
|
||||
}
|
||||
|
||||
public function deleteNonassignedImages(): void
|
||||
{
|
||||
$results = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
|
||||
$this->db->delete( 'pp_articles_images', [ 'article_id' => null ] );
|
||||
}
|
||||
|
||||
public function duplicateArticle( $articleId, $userId ): bool
|
||||
{
|
||||
$article = $this->articleDetails( $articleId );
|
||||
|
||||
if ( !$article )
|
||||
return false;
|
||||
|
||||
$this->db->insert( 'pp_articles', [
|
||||
'show_title' => $article['show_title'],
|
||||
'show_date_add' => $article['show_date_add'],
|
||||
'show_date_modify' => $article['show_date_modify'],
|
||||
'date_add' => date( 'Y-m-d H:i:s' ),
|
||||
'date_modify' => date( 'Y-m-d H:i:s' ),
|
||||
'modify_by' => $userId,
|
||||
'layout_id' => $article['layout_id'],
|
||||
'status' => $article['status'],
|
||||
'repeat_entry' => $article['repeat_entry'],
|
||||
'social_icons' => $article['social_icons'],
|
||||
'date_start' => $article['date_start'],
|
||||
'date_end' => $article['event_date'],
|
||||
'priority' => $article['priority'],
|
||||
'password' => $article['password'],
|
||||
'pixieset' => $article['pixieset']
|
||||
] );
|
||||
|
||||
$articleTmpId = $this->db->id();
|
||||
if ( !$articleTmpId )
|
||||
return false;
|
||||
|
||||
foreach ( $article['languages'] as $key => $val )
|
||||
$this->db->insert( 'pp_articles_langs', [
|
||||
'article_id' => $articleTmpId,
|
||||
'lang_id' => $key,
|
||||
'title' => 'Kopia: ' . $val['title'],
|
||||
'entry' => $val['entry'],
|
||||
'text' => $val['text'],
|
||||
'meta_title' => null,
|
||||
'meta_description' => null,
|
||||
'meta_keywords' => null,
|
||||
'seo_link' => null,
|
||||
'copy_from' => $val['copy_from'],
|
||||
'block_direct_access' => $val['block_direct_access']
|
||||
] );
|
||||
|
||||
foreach ( $article['params'] as $param )
|
||||
$this->db->insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $param['param_id'],
|
||||
'value' => $param['value'],
|
||||
'article_id' => $articleTmpId,
|
||||
'language_id' => $param['language_id']
|
||||
] );
|
||||
|
||||
foreach ( $article['pages'] as $page )
|
||||
{
|
||||
$order = $this->maxOrder() + 1;
|
||||
$this->db->insert( 'pp_articles_pages', [
|
||||
'article_id' => $articleTmpId,
|
||||
'page_id' => $page,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function articleSave(
|
||||
$articleId, $title, $mainImage, $entry, $text, $tableOfContents, $status, $showTitle, $showTableOfContents, $showDateAdd, $dateAdd, $showDateModify, $dateModify, $seoLink, $metaTitle, $metaDescription,
|
||||
$metaKeywords, $layoutId, $pages, $noindex, $repeatEntry, $copyFrom, $socialIcons, $eventDate, $tags, $blockDirectAccess, $priority, $password, $pixieset, $idAuthor, $params, $userId
|
||||
)
|
||||
{
|
||||
$eventDate = explode( ' - ', $eventDate );
|
||||
|
||||
if ( !$articleId )
|
||||
{
|
||||
$this->db->insert( 'pp_articles', [
|
||||
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||
'show_table_of_contents' => $showTableOfContents == 'on' ? 1 : 0,
|
||||
'show_date_add' => $showDateAdd == 'on' ? 1 : 0,
|
||||
'show_date_modify' => $showDateModify == 'on' ? 1 : 0,
|
||||
'date_add' => date( 'Y-m-d H:i:s' ),
|
||||
'date_modify' => date( 'Y-m-d H:i:s' ),
|
||||
'modify_by' => $userId,
|
||||
'layout_id' => $layoutId ? (int)$layoutId : null,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'repeat_entry' => $repeatEntry == 'on' ? 1 : 0,
|
||||
'social_icons' => $socialIcons == 'on' ? 1 : 0,
|
||||
'date_start' => $eventDate[0] ? $eventDate[0] : null,
|
||||
'date_end' => $eventDate[1] ? $eventDate[1] : null,
|
||||
'priority' => $priority == 'on' ? 1 : 0,
|
||||
'password' => $password ? $password : null,
|
||||
'pixieset' => $pixieset,
|
||||
'id_author' => $idAuthor ? $idAuthor : null
|
||||
] );
|
||||
|
||||
$id = $this->db->id();
|
||||
if ( !$id )
|
||||
return false;
|
||||
|
||||
$i = 0;
|
||||
$results = $this->db->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
||||
'main_image' => $mainImage[$i] != '' ? $mainImage[$i] : null,
|
||||
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
||||
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
||||
'table_of_contents' => $tableOfContents[$i] != '' ? $tableOfContents[$i] : null,
|
||||
'meta_title' => $metaTitle[ $i ] != '' ? $metaTitle[ $i ] : null,
|
||||
'meta_description' => $metaDescription[ $i ] != '' ? $metaDescription[ $i ] : null,
|
||||
'meta_keywords' => $metaKeywords[ $i ] != '' ? $metaKeywords[ $i ] : null,
|
||||
'seo_link' => \S::seo( $seoLink[ $i ] ) != '' ? \S::seo( $seoLink[ $i ] ) : null,
|
||||
'noindex' => $noindex[ $i ],
|
||||
'copy_from' => $copyFrom[ $i ] != '' ? $copyFrom[ $i ] : null,
|
||||
'block_direct_access' => $blockDirectAccess[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title != '' ? $title : null,
|
||||
'main_image' => $mainImage != '' ? $mainImage : null,
|
||||
'entry' => $entry != '' ? $entry : null,
|
||||
'text' => $text != '' ? $text : null,
|
||||
'table_of_contents' => $tableOfContents != '' ? $tableOfContents : null,
|
||||
'meta_title' => $metaTitle != '' ? $metaTitle : null,
|
||||
'meta_description' => $metaDescription != '' ? $metaDescription : null,
|
||||
'meta_keywords' => $metaKeywords != '' ? $metaKeywords : null,
|
||||
'seo_link' => \S::seo( $seoLink ) != '' ? \S::seo( $seoLink ) : null,
|
||||
'noindex' => $noindex,
|
||||
'copy_from' => $copyFrom != '' ? $copyFrom : null,
|
||||
'block_direct_access' => $blockDirectAccess
|
||||
] );
|
||||
}
|
||||
|
||||
$results = $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $row['id'],
|
||||
'value' => $params[ 'ap_' . $row['name'] ],
|
||||
'article_id' => (int)$id,
|
||||
'language_id' => null
|
||||
] );
|
||||
}
|
||||
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
{
|
||||
$order = $this->maxOrder() + 1;
|
||||
$this->db->insert( 'pp_articles_pages', [
|
||||
'article_id' => (int)$id,
|
||||
'page_id' => (int)$page,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
else if ( $pages )
|
||||
{
|
||||
$order = $this->maxOrder() + 1;
|
||||
$this->db->insert( 'pp_articles_pages', [
|
||||
'article_id' => (int)$id,
|
||||
'page_id' => (int)$pages,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
|
||||
$results = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_files/article_' . $id;
|
||||
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$this->db->update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => $id ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$created = false;
|
||||
|
||||
$results = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_images/article_' . $id;
|
||||
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '../' . $new_file_name ) )
|
||||
{
|
||||
$ext = strrpos( $new_file_name, '.' );
|
||||
$fileName_a = substr( $new_file_name, 0, $ext );
|
||||
$fileName_b = substr( $new_file_name, $ext );
|
||||
$count = 1;
|
||||
|
||||
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
||||
$count++;
|
||||
|
||||
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
||||
}
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$this->db->update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$id ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$tags = explode( ',', $tags );
|
||||
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
||||
{
|
||||
if ( trim( $tag ) != '' )
|
||||
{
|
||||
$tag_id = $this->db->get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
||||
if ( !$tag_id )
|
||||
{
|
||||
$this->db->insert( 'pp_tags', [ 'name' => $tag ] );
|
||||
$tag_id = $this->db->id();
|
||||
}
|
||||
|
||||
$this->db->insert( 'pp_articles_tags', [ 'article_id' => (int)$id, 'tag_id' => (int)$tag_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->update( 'pp_articles', [
|
||||
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||
'show_table_of_contents' => $showTableOfContents == 'on' ? 1 : 0,
|
||||
'show_date_add' => $showDateAdd == 'on' ? 1 : 0,
|
||||
'date_add' => $dateAdd,
|
||||
'show_date_modify' => $showDateModify == 'on' ? 1 : 0,
|
||||
'date_modify' => $dateModify ? $dateModify : date( 'Y-m-d H:i:s' ),
|
||||
'modify_by' => $userId,
|
||||
'layout_id' => $layoutId ? (int)$layoutId : null,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'repeat_entry' => $repeatEntry == 'on' ? 1 : 0,
|
||||
'social_icons' => $socialIcons == 'on' ? 1 : 0,
|
||||
'date_start' => $eventDate[0] ? $eventDate[0] : null,
|
||||
'date_end' => $eventDate[1] ? $eventDate[1] : null,
|
||||
'priority' => $priority == 'on' ? 1 : 0,
|
||||
'password' => $password ? $password : null,
|
||||
'pixieset' => $pixieset,
|
||||
'id_author' => $idAuthor ? $idAuthor : null
|
||||
], [
|
||||
'id' => (int)$articleId
|
||||
] );
|
||||
|
||||
if ( $dateAdd )
|
||||
$this->db->update( 'pp_articles', [ 'date_add' => $dateAdd ], [ 'id' => (int)$articleId ] );
|
||||
|
||||
$i = 0;
|
||||
$this->db->delete( 'pp_articles_langs', [ 'article_id' => (int)$articleId ] );
|
||||
|
||||
$results = $this->db->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$articleId,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
||||
'main_image' => $mainImage[$i] != '' ? $mainImage[$i] : null,
|
||||
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
||||
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
||||
'table_of_contents' => $tableOfContents[$i] != '' ? $tableOfContents[$i] : null,
|
||||
'meta_title' => $metaTitle[ $i ] != '' ? $metaTitle[ $i ] : null,
|
||||
'meta_description' => $metaDescription[ $i ] != '' ? $metaDescription[ $i ] : null,
|
||||
'meta_keywords' => $metaKeywords[ $i ] != '' ? $metaKeywords[ $i ] : null,
|
||||
'seo_link' => \S::seo( $seoLink[ $i ] ) != '' ? \S::seo( $seoLink[ $i ] ) : null,
|
||||
'noindex' => $noindex[ $i ],
|
||||
'copy_from' => $copyFrom[ $i ] != '' ? $copyFrom[ $i ] : null,
|
||||
'block_direct_access' => $blockDirectAccess[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$articleId,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title != '' ? $title : null,
|
||||
'main_image' => $mainImage != '' ? $mainImage : null,
|
||||
'entry' => $entry != '' ? $entry : null,
|
||||
'text' => $text != '' ? $text : null,
|
||||
'table_of_contents' => $tableOfContents != '' ? $tableOfContents : null,
|
||||
'meta_title' => $metaTitle != '' ? $metaTitle : null,
|
||||
'meta_description' => $metaDescription != '' ? $metaDescription : null,
|
||||
'meta_keywords' => $metaKeywords != '' ? $metaKeywords : null,
|
||||
'seo_link' => \S::seo( $seoLink ) != '' ? \S::seo( $seoLink ) : null,
|
||||
'noindex' => $noindex,
|
||||
'copy_from' => $copyFrom != '' ? $copyFrom : null,
|
||||
'block_direct_access' => $blockDirectAccess
|
||||
] );
|
||||
}
|
||||
|
||||
$this->db->delete( 'pp_articles_additional_values', [ 'article_id' => (int)$articleId ] );
|
||||
|
||||
$results = $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $row['id'],
|
||||
'value' => $params[ 'ap_' . $row['name'] ],
|
||||
'article_id' => (int)$articleId,
|
||||
'language_id' => null
|
||||
] );
|
||||
}
|
||||
|
||||
$results = $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 1 ] ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$results2 = $this->db->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results2 ) ) foreach ( $results2 as $row2 )
|
||||
{
|
||||
$this->db->insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $row['id'],
|
||||
'value' => $params[ 'ap_' . $row['name'] . '_' . $row2['id'] ],
|
||||
'article_id' => (int)$articleId,
|
||||
'language_id' => $row2['id']
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
$not_in = [ 0 ];
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
$not_in[] = $page;
|
||||
else if ( $pages )
|
||||
$not_in[] = $pages;
|
||||
|
||||
$this->db->delete( 'pp_articles_pages', [ 'AND' => [ 'article_id' => (int)$articleId, 'page_id[!]' => $not_in ] ] );
|
||||
|
||||
$pages_tmp = $this->db->select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$articleId ] );
|
||||
if ( !is_array( $pages ) )
|
||||
$pages = [ $pages ];
|
||||
|
||||
$pages = array_diff( $pages, $pages_tmp );
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
{
|
||||
$order = $this->maxOrder() + 1;
|
||||
$this->db->insert( 'pp_articles_pages', [
|
||||
'article_id' => (int)$articleId,
|
||||
'page_id' => (int)$page,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
|
||||
$results = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_files/article_' . $articleId;
|
||||
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$this->db->update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => (int)$articleId ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$created = false;
|
||||
|
||||
$results = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_images/article_' . $articleId;
|
||||
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '../' . $new_file_name ) )
|
||||
{
|
||||
$ext = strrpos( $new_file_name, '.' );
|
||||
$fileName_a = substr( $new_file_name, 0, $ext );
|
||||
$fileName_b = substr( $new_file_name, $ext );
|
||||
$count = 1;
|
||||
|
||||
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
||||
$count++;
|
||||
|
||||
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
||||
}
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$this->db->update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$articleId ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$results = $this->db->select( 'pp_articles_images', '*', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
|
||||
$this->db->delete( 'pp_articles_images', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||
|
||||
$results = $this->db->select( 'pp_articles_files', '*', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
|
||||
$this->db->delete( 'pp_articles_files', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||
|
||||
$this->db->delete( 'pp_articles_tags', [ 'article_id' => (int)$articleId ] );
|
||||
|
||||
$tags = explode( ',', $tags );
|
||||
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
||||
{
|
||||
if ( trim( $tag ) != '' )
|
||||
{
|
||||
$tag_id = $this->db->get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
||||
if ( !$tag_id )
|
||||
{
|
||||
$this->db->insert( 'pp_tags', [ 'name' => $tag ] );
|
||||
$tag_id = $this->db->id();
|
||||
}
|
||||
|
||||
$this->db->insert( 'pp_articles_tags', [ 'article_id' => (int)$articleId, 'tag_id' => (int)$tag_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
return $articleId;
|
||||
}
|
||||
}
|
||||
|
||||
public function maxOrder()
|
||||
{
|
||||
return $this->db->max( 'pp_articles_pages', 'o' );
|
||||
}
|
||||
}
|
||||
?>
|
||||
123
autoload/Domain/Layouts/LayoutsRepository.php
Normal file
123
autoload/Domain/Layouts/LayoutsRepository.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace Domain\Layouts;
|
||||
|
||||
class LayoutsRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function layoutDelete( $layoutId )
|
||||
{
|
||||
if ( $this->db->count( 'pp_layouts' ) > 1 )
|
||||
return $this->db->delete( 'pp_layouts', [ 'id' => (int)$layoutId ] );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function layoutDetails( $layoutId )
|
||||
{
|
||||
$layout = $this->db->get( 'pp_layouts', '*', [ 'id' => (int)$layoutId ] );
|
||||
$layout['pages'] = $this->db->select( 'pp_layouts_pages', 'page_id', [ 'layout_id' => (int)$layoutId ] );
|
||||
|
||||
return $layout;
|
||||
}
|
||||
|
||||
public function layoutSave( $layoutId, $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs )
|
||||
{
|
||||
if ( !$layoutId )
|
||||
return $this->createLayout( $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs );
|
||||
|
||||
return $this->updateLayout( $layoutId, $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs );
|
||||
}
|
||||
|
||||
public function menusList()
|
||||
{
|
||||
$pagesRepo = new \Domain\Pages\PagesRepository( $this->db );
|
||||
$results = $this->db->select( 'pp_menus', 'id', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$menu = $pagesRepo->menuDetails( $row );
|
||||
$menu['pages'] = $pagesRepo->menuPages( $row );
|
||||
$menus[] = $menu;
|
||||
}
|
||||
|
||||
return isset( $menus ) ? $menus : null;
|
||||
}
|
||||
|
||||
public function layoutsList()
|
||||
{
|
||||
return $this->db->select( 'pp_layouts', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
}
|
||||
|
||||
private function createLayout( $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs )
|
||||
{
|
||||
if ( $status == 'on' )
|
||||
$this->db->update( 'pp_layouts', [ 'status' => 0 ] );
|
||||
|
||||
$this->db->insert( 'pp_layouts', [
|
||||
'name' => $name,
|
||||
'html' => $html,
|
||||
'css' => $css,
|
||||
'js' => $js,
|
||||
'm_html' => $mHtml,
|
||||
'm_css' => $mCss,
|
||||
'm_js' => $mJs,
|
||||
'status' => $status == 'on' ? 1 : 0
|
||||
] );
|
||||
|
||||
$id = $this->db->id();
|
||||
if ( !$id )
|
||||
return false;
|
||||
|
||||
$this->replaceLayoutPages( (int)$id, $pages );
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function updateLayout( $layoutId, $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs )
|
||||
{
|
||||
if ( $status == 'on' )
|
||||
$this->db->update( 'pp_layouts', [ 'status' => 0 ] );
|
||||
|
||||
$this->db->update( 'pp_layouts', [
|
||||
'name' => $name,
|
||||
'html' => $html,
|
||||
'css' => $css,
|
||||
'js' => $js,
|
||||
'm_html' => $mHtml,
|
||||
'm_css' => $mCss,
|
||||
'm_js' => $mJs,
|
||||
'status' => $status == 'on' ? 1 : 0
|
||||
], [
|
||||
'id' => $layoutId
|
||||
] );
|
||||
|
||||
$this->db->delete( 'pp_layouts_pages', [ 'layout_id' => (int)$layoutId ] );
|
||||
$this->replaceLayoutPages( (int)$layoutId, $pages );
|
||||
|
||||
\S::delete_cache();
|
||||
return $layoutId;
|
||||
}
|
||||
|
||||
private function replaceLayoutPages( int $layoutId, $pages ): void
|
||||
{
|
||||
if ( is_array( $pages ) )
|
||||
foreach ( $pages as $page )
|
||||
{
|
||||
$this->db->delete( 'pp_layouts_pages', [ 'page_id' => (int)$page ] );
|
||||
$this->db->insert( 'pp_layouts_pages', [ 'layout_id' => $layoutId, 'page_id' => (int)$page ] );
|
||||
}
|
||||
else if ( $pages )
|
||||
{
|
||||
$this->db->delete( 'pp_layouts_pages', [ 'page_id' => (int)$pages ] );
|
||||
$this->db->insert( 'pp_layouts_pages', [ 'layout_id' => $layoutId, 'page_id' => (int)$pages ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
451
autoload/Domain/Pages/PagesRepository.php
Normal file
451
autoload/Domain/Pages/PagesRepository.php
Normal file
@@ -0,0 +1,451 @@
|
||||
<?php
|
||||
namespace Domain\Pages;
|
||||
|
||||
class PagesRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function saveArticlesOrder( $pageId, $articles ): bool
|
||||
{
|
||||
if ( is_array( $articles ) )
|
||||
{
|
||||
$this->db->update( 'pp_articles_pages', [ 'o' => 0 ], [ 'page_id' => (int) $pageId ] );
|
||||
$x = 0;
|
||||
|
||||
for ( $i = 0; $i < count( $articles ); $i++ )
|
||||
{
|
||||
if ( $articles[$i]['item_id'] )
|
||||
{
|
||||
$x++;
|
||||
$this->db->update( 'pp_articles_pages', [ 'o' => $x ], [
|
||||
'AND' => [ 'page_id' => (int) $pageId, 'article_id' => $articles[$i]['item_id'] ]
|
||||
] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function pageArticles( $pageId )
|
||||
{
|
||||
$results = $this->db->query(
|
||||
'SELECT article_id, o, status FROM pp_articles_pages AS ap INNER JOIN pp_articles AS a ON a.id = ap.article_id WHERE page_id = ' . (int) $pageId . ' AND status != -1 ORDER BY o ASC'
|
||||
)->fetchAll();
|
||||
|
||||
$articles = [];
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$row['title'] = \admin\factory\Articles::article_title( $row['article_id'] );
|
||||
$articles[] = $row;
|
||||
}
|
||||
|
||||
return $articles;
|
||||
}
|
||||
|
||||
public function menusList()
|
||||
{
|
||||
return $this->db->select( 'pp_menus', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
}
|
||||
|
||||
public function savePagesOrder( $menuId, $pages ): bool
|
||||
{
|
||||
if ( is_array( $pages ) )
|
||||
{
|
||||
$this->db->update( 'pp_pages', [ 'o' => 0 ], [ 'menu_id' => (int) $menuId ] );
|
||||
$x = 0;
|
||||
|
||||
for ( $i = 0; $i < count( $pages ); $i++ )
|
||||
{
|
||||
if ( $pages[$i]['item_id'] )
|
||||
{
|
||||
$parentId = $pages[$i]['parent_id'] ? $pages[$i]['parent_id'] : 0;
|
||||
|
||||
if ( $pages[$i]['item_id'] && $pages[$i]['depth'] > 1 )
|
||||
{
|
||||
if ( $pages[$i]['depth'] == 2 )
|
||||
$parentId = null;
|
||||
|
||||
$x++;
|
||||
$this->db->update( 'pp_pages', [ 'o' => $x, 'parent_id' => $parentId ], [ 'id' => (int) $pages[$i]['item_id'] ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function pageDelete( $pageId ): bool
|
||||
{
|
||||
if ( $this->db->count( 'pp_pages', [ 'parent_id' => (int) $pageId ] ) )
|
||||
return false;
|
||||
|
||||
if ( $this->db->delete( 'pp_pages', [ 'id' => (int) $pageId ] ) )
|
||||
{
|
||||
\S::delete_cache();
|
||||
\S::htacces();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function maxOrder(): int
|
||||
{
|
||||
return (int) $this->db->max( 'pp_pages', 'o' );
|
||||
}
|
||||
|
||||
public function updateSubpagesMenuId( int $parentId, int $menuId ): void
|
||||
{
|
||||
$this->updateSubpagesMenuIdRecursive( $parentId, $menuId );
|
||||
}
|
||||
|
||||
public function generateSeoLink( $title, $pageId, $articleId, $lang, $pid )
|
||||
{
|
||||
$seoLink = \S::seo( $title );
|
||||
$seoLinkCheck = false;
|
||||
$i = 0;
|
||||
|
||||
while ( !$seoLinkCheck )
|
||||
{
|
||||
if ( $this->db->count( 'pp_pages_langs', [ 'AND' => [ 'seo_link' => $seoLink, 'page_id[!]' => (int) $pageId ] ] ) )
|
||||
$seoLink = $seoLink . '-' . ( ++$i );
|
||||
else
|
||||
$seoLinkCheck = true;
|
||||
}
|
||||
|
||||
$seoLinkCheck = false;
|
||||
|
||||
while ( !$seoLinkCheck )
|
||||
{
|
||||
if ( $this->db->count( 'pp_articles_langs', [ 'AND' => [ 'seo_link' => $seoLink, 'article_id[!]' => (int) $articleId ] ] ) )
|
||||
$seoLink = $seoLink . '-' . ( ++$i );
|
||||
else
|
||||
$seoLinkCheck = true;
|
||||
}
|
||||
|
||||
return $seoLink;
|
||||
}
|
||||
|
||||
public function googleUrlPreview( $pageId, $title, $lang, $pid, $id, $seoLink, $languageLink = '' )
|
||||
{
|
||||
$prefix = $languageLink;
|
||||
$status = true;
|
||||
$idPage = $pageId;
|
||||
$seo = '';
|
||||
|
||||
do
|
||||
{
|
||||
if ( $pageId )
|
||||
{
|
||||
$parent = $this->pageDetails( $pageId );
|
||||
$parentId = $parent['parent_id'];
|
||||
}
|
||||
else
|
||||
$parentId = $pid;
|
||||
|
||||
if ( $parentId )
|
||||
{
|
||||
$results = $this->db->query(
|
||||
"SELECT title, seo_link, page_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $parentId . " AND ppl.lang_id = '" . $lang . "' "
|
||||
)->fetchAll();
|
||||
if ( $results[0]['seo_link'] )
|
||||
$seo = $results[0]['seo_link'] . '/' . $seo;
|
||||
else
|
||||
$seo = 's-' . $results[0]['page_id'] . '-' . \S::seo( $results[0]['title'] ) . '/' . $seo;
|
||||
$pageId = $results[0]['page_id'];
|
||||
}
|
||||
else
|
||||
$status = false;
|
||||
}
|
||||
while ( $status );
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
if ( !$seoLink )
|
||||
$seo = $seo . 's-' . $id . '-' . \S::seo( $title );
|
||||
else
|
||||
$seo = $seo . $seoLink;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !$seoLink )
|
||||
$seo = $seo . 's-' . $idPage . '-' . \S::seo( $title );
|
||||
else
|
||||
$seo = $seo . $seoLink;
|
||||
}
|
||||
|
||||
if ( $prefix )
|
||||
$seo = $prefix . $seo;
|
||||
|
||||
return $seo;
|
||||
}
|
||||
|
||||
public function menuDelete( $menuId )
|
||||
{
|
||||
if ( $this->db->count( 'pp_pages', [ 'menu_id' => (int) $menuId ] ) )
|
||||
return false;
|
||||
|
||||
return $this->db->delete( 'pp_menus', [ 'id' => (int) $menuId ] );
|
||||
}
|
||||
|
||||
public function menuDetails( $menuId )
|
||||
{
|
||||
return $this->db->get( 'pp_menus', '*', [ 'id' => (int) $menuId ] );
|
||||
}
|
||||
|
||||
public function menuSave( $menuId, $name, $status )
|
||||
{
|
||||
$status == 'on' ? $status = 1 : $status = 0;
|
||||
|
||||
if ( !$menuId )
|
||||
{
|
||||
return $this->db->insert( 'pp_menus', [ 'name' => $name, 'status' => $status ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->update( 'pp_menus', [ 'name' => $name, 'status' => $status ], [ 'id' => (int) $menuId ] );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function menuLists()
|
||||
{
|
||||
return $this->db->select( 'pp_menus', '*', [ 'ORDER' => [ 'id' => 'ASC' ] ] );
|
||||
}
|
||||
|
||||
public function pageDetails( $pageId )
|
||||
{
|
||||
$page = $this->db->get( 'pp_pages', '*', [ 'id' => (int) $pageId ] );
|
||||
|
||||
$results = $this->db->select( 'pp_pages_langs', '*', [ 'page_id' => (int) $pageId ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$page['languages'][$row['lang_id']] = $row;
|
||||
|
||||
$page['layout_id'] = $this->db->get( 'pp_layouts_pages', 'layout_id', [ 'page_id' => (int) $pageId ] );
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
public function pageUrl( $pageId )
|
||||
{
|
||||
$results = $this->db->query(
|
||||
"SELECT seo_link, title lang_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $pageId . " AND seo_link != '' ORDER BY o ASC LIMIT 1"
|
||||
)->fetchAll();
|
||||
|
||||
if ( !$results[0]['seo_link'] )
|
||||
{
|
||||
$title = $this->pageTitle( $article_id );
|
||||
return 's-' . $pageId . '-' . \S::seo( $title );
|
||||
}
|
||||
else
|
||||
return $results[0]['seo_link'];
|
||||
}
|
||||
|
||||
public function pageTitle( $pageId )
|
||||
{
|
||||
$result = $this->db->select( 'pp_pages_langs', [ '[><]pp_langs' => [ 'lang_id' => 'id' ] ], 'title', [
|
||||
'AND' => [ 'page_id' => (int) $pageId, 'title[!]' => '' ], 'ORDER' => [ 'o' => 'ASC' ], 'LIMIT' => 1
|
||||
] );
|
||||
return $result[0];
|
||||
}
|
||||
|
||||
public function pageLanguages( $pageId )
|
||||
{
|
||||
return $this->db->select( 'pp_pages_langs', '*', [ 'AND' => [ 'page_id' => (int) $pageId, 'title[!]' => null ] ] );
|
||||
}
|
||||
|
||||
public function menuPages( $menuId, $parentId = null )
|
||||
{
|
||||
$results = $this->db->select( 'pp_pages', [ 'id', 'menu_id', 'status', 'parent_id', 'start' ], [
|
||||
'AND' => [ 'menu_id' => $menuId, 'parent_id' => $parentId ], 'ORDER' => [ 'o' => 'ASC' ]
|
||||
] );
|
||||
|
||||
if ( !is_array( $results ) || !count( $results ) )
|
||||
return null;
|
||||
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$row['title'] = $this->pageTitle( $row['id'] );
|
||||
$row['languages'] = $this->pageLanguages( $row['id'] );
|
||||
$row['subpages'] = $this->menuPages( $menuId, $row['id'] );
|
||||
|
||||
$pages[] = $row;
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
public function pageSave(
|
||||
$pageId, $title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||
)
|
||||
{
|
||||
$parentId = $parentId ? $parentId : null;
|
||||
|
||||
if ( !$pageId )
|
||||
return $this->createPage(
|
||||
$title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||
);
|
||||
|
||||
return $this->updatePage(
|
||||
$pageId, $title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||
);
|
||||
}
|
||||
|
||||
private function createPage(
|
||||
$title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start, $siteTitle,
|
||||
$blockDirectAccess, $cache, $canonical
|
||||
)
|
||||
{
|
||||
$order = $this->maxOrder() + 1;
|
||||
|
||||
$this->db->insert( 'pp_pages', [
|
||||
'menu_id' => (int) $menuId,
|
||||
'page_type' => $pageType,
|
||||
'sort_type' => $sortType,
|
||||
'articles_limit' => $articlesLimit,
|
||||
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'o' => (int) $order,
|
||||
'parent_id' => $parentId,
|
||||
'start' => $start == 'on' ? 1 : 0,
|
||||
'cache' => $cache == 'on' ? 1 : 0,
|
||||
] );
|
||||
|
||||
$id = $this->db->id();
|
||||
if ( !$id )
|
||||
return false;
|
||||
|
||||
if ( $start )
|
||||
$this->db->update( 'pp_pages', [ 'start' => 0 ], [ 'id[!]' => (int) $id ] );
|
||||
|
||||
if ( $layoutId )
|
||||
$this->db->insert( 'pp_layouts_pages', [ 'page_id' => (int) $id, 'layout_id' => (int) $layoutId ] );
|
||||
|
||||
$languages = $this->activeLanguages();
|
||||
$this->savePageLanguages( (int) $id, $languages, $title, $metaDescription, $metaKeywords, $metaTitle, $seoLink, $noindex, $siteTitle, $link, $blockDirectAccess, $canonical );
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function updatePage(
|
||||
$pageId, $title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||
)
|
||||
{
|
||||
$this->db->update( 'pp_pages', [
|
||||
'menu_id' => (int) $menuId,
|
||||
'page_type' => $pageType,
|
||||
'sort_type' => $sortType,
|
||||
'articles_limit' => $articlesLimit,
|
||||
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'parent_id' => $parentId,
|
||||
'start' => $start == 'on' ? 1 : 0,
|
||||
'cache' => $cache == 'on' ? 1 : 0,
|
||||
], [
|
||||
'id' => (int) $pageId
|
||||
] );
|
||||
|
||||
if ( $layoutId )
|
||||
{
|
||||
$this->db->delete( 'pp_layouts_pages', [ 'page_id' => (int) $pageId ] );
|
||||
$this->db->insert( 'pp_layouts_pages', [ 'layout_id' => (int) $layoutId, 'page_id' => (int) $pageId ] );
|
||||
}
|
||||
|
||||
if ( $start )
|
||||
$this->db->update( 'pp_pages', [ 'start' => 0 ], [ 'id[!]' => (int) $pageId ] );
|
||||
|
||||
$this->db->delete( 'pp_pages_langs', [ 'page_id' => (int) $pageId ] );
|
||||
|
||||
$languages = $this->activeLanguages();
|
||||
$this->savePageLanguages( (int) $pageId, $languages, $title, $metaDescription, $metaKeywords, $metaTitle, $seoLink, $noindex, $siteTitle, $link, $blockDirectAccess, $canonical );
|
||||
|
||||
$this->updateSubpagesMenuIdRecursive( (int) $pageId, (int) $menuId );
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $pageId;
|
||||
}
|
||||
|
||||
private function activeLanguages(): array
|
||||
{
|
||||
return $this->db->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] ) ?: [];
|
||||
}
|
||||
|
||||
private function savePageLanguages(
|
||||
int $pageId, array $languages, $title, $metaDescription, $metaKeywords, $metaTitle, $seoLink, $noindex, $siteTitle, $link, $blockDirectAccess, $canonical
|
||||
): void
|
||||
{
|
||||
$isMulti = count( $languages ) > 1;
|
||||
|
||||
foreach ( $languages as $i => $row )
|
||||
{
|
||||
$titleValue = $this->languageValue( $title, $i, $isMulti );
|
||||
$metaDescriptionValue = $this->languageValue( $metaDescription, $i, $isMulti );
|
||||
$metaKeywordsValue = $this->languageValue( $metaKeywords, $i, $isMulti );
|
||||
$metaTitleValue = $this->languageValue( $metaTitle, $i, $isMulti );
|
||||
$seoLinkValue = $this->languageValue( $seoLink, $i, $isMulti );
|
||||
$noindexValue = $this->languageValue( $noindex, $i, $isMulti );
|
||||
$siteTitleValue = $this->languageValue( $siteTitle, $i, $isMulti );
|
||||
$linkValue = $this->languageValue( $link, $i, $isMulti );
|
||||
$blockDirectAccessValue = $this->languageValue( $blockDirectAccess, $i, $isMulti );
|
||||
$canonicalValue = $this->languageValue( $canonical, $i, $isMulti );
|
||||
|
||||
$seo = \S::seo( $seoLinkValue );
|
||||
|
||||
$this->db->insert( 'pp_pages_langs', [
|
||||
'page_id' => $pageId,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $this->nullIfEmpty( $titleValue ),
|
||||
'meta_description' => $this->nullIfEmpty( $metaDescriptionValue ),
|
||||
'meta_keywords' => $this->nullIfEmpty( $metaKeywordsValue ),
|
||||
'meta_title' => $this->nullIfEmpty( $metaTitleValue ),
|
||||
'seo_link' => $seo != '' ? $seo : null,
|
||||
'noindex' => $noindexValue,
|
||||
'site_title' => $this->nullIfEmpty( $siteTitleValue ),
|
||||
'link' => $this->nullIfEmpty( $linkValue ),
|
||||
'block_direct_access' => $blockDirectAccessValue,
|
||||
'canonical' => $this->nullIfEmpty( $canonicalValue )
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
private function languageValue( $value, int $index, bool $isMulti )
|
||||
{
|
||||
if ( $isMulti )
|
||||
return is_array( $value ) ? ( $value[$index] ?? null ) : null;
|
||||
|
||||
return is_array( $value ) ? ( $value[0] ?? null ) : $value;
|
||||
}
|
||||
|
||||
private function nullIfEmpty( $value )
|
||||
{
|
||||
return $value != '' ? $value : null;
|
||||
}
|
||||
|
||||
private function updateSubpagesMenuIdRecursive( int $parentId, int $menuId ): void
|
||||
{
|
||||
$this->db->update( 'pp_pages', [ 'menu_id' => $menuId ], [ 'parent_id' => $parentId ] );
|
||||
|
||||
$results = $this->db->select( 'pp_pages', [ 'id' ], [ 'parent_id' => $parentId ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$this->updateSubpagesMenuIdRecursive( (int) $row['id'], $menuId );
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,13 @@ class SettingsRepository
|
||||
{
|
||||
if ( !$settings = \Shared\Cache\CacheHandler::fetch( 'settings_details' ) )
|
||||
{
|
||||
$results = $this->db->select( 'pp_settings', '*' );
|
||||
$settings = [];
|
||||
$results = $this->db->select( 'pp_settings', '*' );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$settings[ $row['param'] ] = $row['value'];
|
||||
|
||||
\Shared\Cache\CacheHandler::store( 'settings_details', $settings ?? [] );
|
||||
\Shared\Cache\CacheHandler::store( 'settings_details', $settings );
|
||||
}
|
||||
|
||||
return $settings ?? [];
|
||||
|
||||
@@ -68,6 +68,11 @@ class Tpl
|
||||
$this->vars[ $name ] = $value;
|
||||
}
|
||||
|
||||
public function __isset( $name )
|
||||
{
|
||||
return isset( $this->vars[ $name ] );
|
||||
}
|
||||
|
||||
public function __get( $name )
|
||||
{
|
||||
return $this->vars[ $name ];
|
||||
|
||||
62
autoload/admin/controls/class.Releases.php
Normal file
62
autoload/admin/controls/class.Releases.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
return \admin\view\Releases::main_view();
|
||||
}
|
||||
|
||||
public static function promote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::promote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function demote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::demote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function discover_versions(): void
|
||||
{
|
||||
$added = \admin\factory\Releases::discover_versions();
|
||||
\S::set_message("Wykryto i dodano {$added} nowych wersji jako stable.");
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function save_license(): void
|
||||
{
|
||||
\admin\factory\Releases::save_license($_POST);
|
||||
\S::set_message('Licencja została zapisana.');
|
||||
header('Location: /admin/releases/main_view/#licenses');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function delete_license(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::delete_license($id);
|
||||
header('Location: /admin/releases/main_view/#licenses');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function toggle_beta(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::toggle_beta($id);
|
||||
header('Location: /admin/releases/main_view/#licenses');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,714 +1,117 @@
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
|
||||
class Articles
|
||||
{
|
||||
public static function duplicate_article( $article_id )
|
||||
private static function repo(): \Domain\Articles\ArticlesRepository
|
||||
{
|
||||
global $mdb, $user;
|
||||
|
||||
$article = \admin\factory\Articles::article_details( $article_id );
|
||||
|
||||
if ( $article )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles', [
|
||||
'show_title' => $article['show_title'],
|
||||
'show_date_add' => $article['show_date_add'],
|
||||
'show_date_modify' => $article['show_date_modify'],
|
||||
'date_add' => date( 'Y-m-d H:i:s' ),
|
||||
'date_modify' => date( 'Y-m-d H:i:s' ),
|
||||
'modify_by' => $user['id'],
|
||||
'layout_id' => $article['layout_id'],
|
||||
'status' => $article['status'],
|
||||
'repeat_entry' => $article['repeat_entry'],
|
||||
'social_icons' => $article['social_icons'],
|
||||
'date_start' => $article['date_start'],
|
||||
'date_end' => $article['event_date'],
|
||||
'priority' => $article['priority'],
|
||||
'password' => $article['password'],
|
||||
'pixieset' => $article['pixieset']
|
||||
] );
|
||||
|
||||
$article_tmp_id = $mdb -> id();
|
||||
|
||||
if ( $article_tmp_id )
|
||||
{
|
||||
foreach ( $article['languages'] as $key => $val )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_langs', [
|
||||
'article_id' => $article_tmp_id,
|
||||
'lang_id' => $key,
|
||||
'title' => 'Kopia: ' . $val['title'],
|
||||
'entry' => $val['entry'],
|
||||
'text' => $val['text'],
|
||||
'meta_title' => null,
|
||||
'meta_description' => null,
|
||||
'meta_keywords' => null,
|
||||
'seo_link' => null,
|
||||
'copy_from' => $val['copy_from'],
|
||||
'block_direct_access' => $val['block_direct_access']
|
||||
] );
|
||||
}
|
||||
|
||||
foreach ( $article['params'] as $param )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $param['param_id'],
|
||||
'value' => $param['value'],
|
||||
'article_id' => $article_tmp_id,
|
||||
'language_id' => $param['language_id']
|
||||
] );
|
||||
}
|
||||
|
||||
foreach ( $article['pages'] as $page )
|
||||
{
|
||||
$order = self::max_order() + 1;
|
||||
$mdb -> insert( 'pp_articles_pages', [
|
||||
'article_id' => $article_tmp_id,
|
||||
'page_id' => $page,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
global $mdb;
|
||||
return new \Domain\Articles\ArticlesRepository( $mdb );
|
||||
}
|
||||
|
||||
public static function insert_missing_hash() {
|
||||
global $mdb;
|
||||
public static function duplicate_article( $article_id )
|
||||
{
|
||||
global $user;
|
||||
return self::repo()->duplicateArticle( $article_id, (int)$user['id'] );
|
||||
}
|
||||
|
||||
if ( $mdb -> count( 'pp_articles', [ 'hash' => null ] ) ) {
|
||||
$rows = $mdb -> select( 'pp_articles', [ 'id', 'date_add' ], [ 'hash' => null ] );
|
||||
if ( is_array( $rows ) ) foreach ( $rows as $row ) {
|
||||
$mdb -> update( 'pp_articles', [ 'hash' => md5( $row['id'] . $row['date_add'] ) ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
public static function insert_missing_hash()
|
||||
{
|
||||
return self::repo()->insertMissingHash();
|
||||
}
|
||||
|
||||
static public function files_order_save( $article_id, $order )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$order = explode( ';', $order );
|
||||
if ( is_array( $order ) and !empty( $order ) ) foreach ( $order as $file_id )
|
||||
{
|
||||
$mdb -> update( 'pp_articles_files', [
|
||||
'o' => (int)$i++
|
||||
], [
|
||||
'AND' => [
|
||||
'article_id' => $article_id,
|
||||
'id' => $file_id
|
||||
]
|
||||
] );
|
||||
}
|
||||
self::repo()->filesOrderSave( $article_id, $order );
|
||||
}
|
||||
|
||||
public static function gallery_order_save( $article_id, $order )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$order = explode( ';', $order );
|
||||
if ( is_array( $order ) and !empty( $order ) ) foreach ( $order as $image_id )
|
||||
{
|
||||
$mdb -> update( 'pp_articles_images', [
|
||||
'o' => $i++
|
||||
], [
|
||||
'AND' => [
|
||||
'article_id' => $article_id,
|
||||
'id' => $image_id
|
||||
]
|
||||
] );
|
||||
}
|
||||
self::repo()->galleryOrderSave( $article_id, $order );
|
||||
}
|
||||
|
||||
public static function additional_params( $language = 0 )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => $language ] ] );
|
||||
return self::repo()->additionalParams( $language );
|
||||
}
|
||||
|
||||
public static function image_alt_change( $image_id, $image_alt )
|
||||
{
|
||||
global $mdb;
|
||||
$result = $mdb -> update( 'pp_articles_images', [
|
||||
'alt' => $image_alt
|
||||
], [
|
||||
'id' => $image_id
|
||||
] );
|
||||
\S::delete_cache();
|
||||
return $result;
|
||||
return self::repo()->imageAltChange( $image_id, $image_alt );
|
||||
}
|
||||
|
||||
public static function articles_by_date_add( $date_start, $date_end )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> query( 'SELECT '
|
||||
. 'id '
|
||||
. 'FROM '
|
||||
. 'pp_articles '
|
||||
. 'WHERE '
|
||||
. 'status = 1 '
|
||||
. 'AND '
|
||||
. 'date_add BETWEEN \'' . $date_start . '\' AND \'' . $date_end . '\' '
|
||||
. 'ORDER BY '
|
||||
. 'date_add DESC' ) -> fetchAll();
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
$articles[] = \front\factory\Articles::article_details( $row['id'], 'pl' );
|
||||
|
||||
return $articles;
|
||||
return self::repo()->articlesByDateAdd( $date_start, $date_end );
|
||||
}
|
||||
|
||||
public static function article_url( $article_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> query( "SELECT seo_link FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$article_id . " AND seo_link != '' ORDER BY o ASC LIMIT 1" ) -> fetchAll();
|
||||
if ( !$results[0]['seo_link'] )
|
||||
{
|
||||
$title = self::article_title( $article_id );
|
||||
return 'a-' . $article_id . '-' . \S::seo( $title );
|
||||
}
|
||||
else
|
||||
return $results[0]['seo_link'];
|
||||
return self::repo()->articleUrl( $article_id );
|
||||
}
|
||||
|
||||
public static function article_pages( $article_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> query( "SELECT page_id FROM pp_articles_pages WHERE article_id = " . (int)$article_id ) -> fetchAll();
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( $out == '' )
|
||||
$out .= ' - ';
|
||||
|
||||
$out .= \admin\factory\Pages::page_title( $row['page_id'] );
|
||||
|
||||
if ( end( $results ) != $row )
|
||||
$out .= ' / ';
|
||||
}
|
||||
|
||||
return $out;
|
||||
return self::repo()->articlePages( $article_id );
|
||||
}
|
||||
|
||||
public static function article_title( $article_id )
|
||||
{
|
||||
global $mdb;
|
||||
$results = $mdb -> query( "SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$article_id . " AND title != '' ORDER BY o ASC LIMIT 1" ) -> fetchAll();
|
||||
return $results[0]['title'];
|
||||
return self::repo()->articleTitle( $article_id );
|
||||
}
|
||||
|
||||
public static function articles_set_archive( $article_id )
|
||||
{
|
||||
global $mdb;
|
||||
$result = $mdb -> update( 'pp_articles', [ 'status' => -1 ], [ 'id' => (int)$article_id ] );
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
return $result;
|
||||
return self::repo()->articlesSetArchive( $article_id );
|
||||
}
|
||||
|
||||
public static function file_name_change( $file_id, $file_name )
|
||||
{
|
||||
global $mdb;
|
||||
$mdb -> update( 'pp_articles_files', [ 'name' => $file_name ], [ 'id' => (int)$file_id ] );
|
||||
return true;
|
||||
return self::repo()->fileNameChange( $file_id, $file_name );
|
||||
}
|
||||
|
||||
public static function delete_file( $file_id )
|
||||
{
|
||||
global $mdb;
|
||||
$mdb -> update( 'pp_articles_files', [ 'to_delete' => 1 ], [ 'id' => (int)$file_id ] );
|
||||
return true;
|
||||
return self::repo()->deleteFile( $file_id );
|
||||
}
|
||||
|
||||
public static function delete_img( $image_id )
|
||||
{
|
||||
global $mdb;
|
||||
$mdb -> update( 'pp_articles_images', [ 'to_delete' => 1 ], [ 'id' => (int)$image_id ] );
|
||||
return true;
|
||||
return self::repo()->deleteImg( $image_id );
|
||||
}
|
||||
|
||||
public static function article_details( $article_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $article = $mdb -> get( 'pp_articles', '*', [ 'id' => (int)$article_id ] ) )
|
||||
{
|
||||
$results = $mdb -> select( 'pp_articles_langs', '*', [ 'article_id' => (int)$article_id ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$article['languages'][ $row['lang_id'] ] = $row;
|
||||
|
||||
$article['images'] = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => (int)$article_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||
$article['files'] = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => (int)$article_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||
$article['pages'] = $mdb -> select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$article_id ] );
|
||||
$article['tags'] = $mdb -> select( 'pp_tags', [ '[><]pp_articles_tags' => [ 'id' => 'tag_id' ] ], 'name', [ 'article_id' => (int)$article_id ] );
|
||||
$article['params'] = $mdb -> select( 'pp_articles_additional_values', [ 'param_id', 'value', 'language_id' ], [ 'article_id' => (int)$article_id ] );
|
||||
}
|
||||
|
||||
return $article;
|
||||
return self::repo()->articleDetails( $article_id );
|
||||
}
|
||||
|
||||
public static function max_order()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> max( 'pp_articles_pages', 'o' );
|
||||
return self::repo()->maxOrder();
|
||||
}
|
||||
|
||||
public static function article_save(
|
||||
$article_id, $title, $main_image, $entry, $text, $table_of_contents, $status, $show_title, $show_table_of_contents, $show_date_add, $date_add, $show_date_modify, $date_modify, $seo_link, $meta_title, $meta_description,
|
||||
$meta_keywords, $layout_id, $pages, $noindex, $repeat_entry, $copy_from, $social_icons, $event_date, $tags, $block_direct_access, $priority,
|
||||
$password, $pixieset, $id_author, $params )
|
||||
$article_id, $title, $main_image, $entry, $text, $table_of_contents, $status, $show_title, $show_table_of_contents, $show_date_add, $date_add, $show_date_modify, $date_modify, $seo_link, $meta_title, $meta_description,
|
||||
$meta_keywords, $layout_id, $pages, $noindex, $repeat_entry, $copy_from, $social_icons, $event_date, $tags, $block_direct_access, $priority,
|
||||
$password, $pixieset, $id_author, $params
|
||||
)
|
||||
{
|
||||
|
||||
global $mdb, $user;
|
||||
|
||||
$event_date = explode( ' - ', $event_date );
|
||||
|
||||
if ( !$article_id )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles', [
|
||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
||||
'show_table_of_contents' => $show_table_of_contents == 'on' ? 1 : 0,
|
||||
'show_date_add' => $show_date_add == 'on' ? 1 : 0,
|
||||
'show_date_modify' => $show_date_modify == 'on' ? 1 : 0,
|
||||
'date_add' => date( 'Y-m-d H:i:s' ),
|
||||
'date_modify' => date( 'Y-m-d H:i:s' ),
|
||||
'modify_by' => $user['id'],
|
||||
'layout_id' => $layout_id ? (int)$layout_id : null,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'repeat_entry' => $repeat_entry == 'on' ? 1 : 0,
|
||||
'social_icons' => $social_icons == 'on' ? 1 : 0,
|
||||
'date_start' => $event_date[0] ? $event_date[0] : null,
|
||||
'date_end' => $event_date[1] ? $event_date[1] : null,
|
||||
'priority' => $priority == 'on' ? 1 : 0,
|
||||
'password' => $password ? $password : null,
|
||||
'pixieset' => $pixieset,
|
||||
'id_author' => $id_author ? $id_author : null
|
||||
] );
|
||||
|
||||
$id = $mdb -> id();
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
$i = 0;
|
||||
|
||||
/* tłumaczenia */
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
||||
'main_image' => $main_image[$i] != '' ? $main_image[$i] : null,
|
||||
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
||||
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
||||
'table_of_contents' => $table_of_contents[$i] != '' ? $table_of_contents[$i] : null,
|
||||
'meta_title' => $meta_title[ $i ] != '' ? $meta_title[ $i ] : null,
|
||||
'meta_description' => $meta_description[ $i ] != '' ? $meta_description[ $i ] : null,
|
||||
'meta_keywords' => $meta_keywords[ $i ] != '' ? $meta_keywords[ $i ] : null,
|
||||
'seo_link' => \S::seo( $seo_link[ $i ] ) != '' ? \S::seo( $seo_link[ $i ] ) : null,
|
||||
'noindex' => $noindex[ $i ],
|
||||
'copy_from' => $copy_from[ $i ] != '' ? $copy_from[ $i ] : null,
|
||||
'block_direct_access' => $block_direct_access[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title != '' ? $title : null,
|
||||
'main_image' => $main_image != '' ? $main_image : null,
|
||||
'entry' => $entry != '' ? $entry : null,
|
||||
'text' => $text != '' ? $text : null,
|
||||
'table_of_contents' => $table_of_contents != '' ? $table_of_contents : null,
|
||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
||||
'noindex' => $noindex,
|
||||
'copy_from' => $copy_from != '' ? $copy_from : null,
|
||||
'block_direct_access' => $block_direct_access
|
||||
] );
|
||||
}
|
||||
|
||||
/* parametry bez wersji językowych */
|
||||
$results = $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $row['id'],
|
||||
'value' => $params[ 'ap_' . $row['name'] ],
|
||||
'article_id' => (int)$id,
|
||||
'language_id' => null
|
||||
] );
|
||||
}
|
||||
|
||||
/* strony */
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
{
|
||||
$order = self::max_order() + 1;
|
||||
|
||||
$mdb -> insert( 'pp_articles_pages', [
|
||||
'article_id' => (int)$id,
|
||||
'page_id' => (int)$page,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
else if ( $pages )
|
||||
{
|
||||
$order = self::max_order() + 1;
|
||||
|
||||
$mdb -> insert( 'pp_articles_pages', [
|
||||
'article_id' => (int)$id,
|
||||
'page_id' => (int)$pages,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
|
||||
/* pliki */
|
||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_files/article_' . $id;
|
||||
|
||||
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$mdb -> update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => $id ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$created = false;
|
||||
|
||||
/* zdjęcia */
|
||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_images/article_' . $id;
|
||||
|
||||
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '../' . $new_file_name ) )
|
||||
{
|
||||
$ext = strrpos( $new_file_name, '.' );
|
||||
$fileName_a = substr( $new_file_name, 0, $ext );
|
||||
$fileName_b = substr( $new_file_name, $ext );
|
||||
|
||||
$count = 1;
|
||||
|
||||
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
||||
$count++;
|
||||
|
||||
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
||||
}
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$mdb -> update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$id ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
/* tagi */
|
||||
$tags = explode( ',', $tags );
|
||||
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
||||
{
|
||||
if ( trim( $tag ) != '' )
|
||||
{
|
||||
$tag_id = $mdb -> get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
||||
if ( !$tag_id )
|
||||
{
|
||||
$mdb -> insert( 'pp_tags', [ 'name' => $tag ] );
|
||||
$tag_id = $mdb -> id();
|
||||
}
|
||||
|
||||
$mdb -> insert( 'pp_articles_tags', [ 'article_id' => (int)$id, 'tag_id' => (int)$tag_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_articles', [
|
||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
||||
'show_table_of_contents' => $show_table_of_contents == 'on' ? 1 : 0,
|
||||
'show_date_add' => $show_date_add == 'on' ? 1 : 0,
|
||||
'date_add' => $date_add,
|
||||
'show_date_modify' => $show_date_modify == 'on' ? 1 : 0,
|
||||
'date_modify' => $date_modify ? $date_modify : date( 'Y-m-d H:i:s' ),
|
||||
'modify_by' => $user['id'],
|
||||
'layout_id' => $layout_id ? (int)$layout_id : null,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'repeat_entry' => $repeat_entry == 'on' ? 1 : 0,
|
||||
'social_icons' => $social_icons == 'on' ? 1 : 0,
|
||||
'date_start' => $event_date[0] ? $event_date[0] : null,
|
||||
'date_end' => $event_date[1] ? $event_date[1] : null,
|
||||
'priority' => $priority == 'on' ? 1 : 0,
|
||||
'password' => $password ? $password : null,
|
||||
'pixieset' => $pixieset,
|
||||
'id_author' => $id_author ? $id_author : null
|
||||
], [
|
||||
'id' => (int)$article_id
|
||||
] );
|
||||
|
||||
if ( $date_add )
|
||||
$mdb -> update( 'pp_articles', [ 'date_add' => $date_add ], [ 'id' => (int)$article_id ] );
|
||||
|
||||
$i = 0;
|
||||
|
||||
/* tłumaczenia */
|
||||
$mdb -> delete( 'pp_articles_langs', [ 'article_id' => (int)$article_id ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$article_id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
||||
'main_image' => $main_image[$i] != '' ? $main_image[$i] : null,
|
||||
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
||||
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
||||
'table_of_contents' => $table_of_contents[$i] != '' ? $table_of_contents[$i] : null,
|
||||
'meta_title' => $meta_title[ $i ] != '' ? $meta_title[ $i ] : null,
|
||||
'meta_description' => $meta_description[ $i ] != '' ? $meta_description[ $i ] : null,
|
||||
'meta_keywords' => $meta_keywords[ $i ] != '' ? $meta_keywords[ $i ] : null,
|
||||
'seo_link' => \S::seo( $seo_link[ $i ] ) != '' ? \S::seo( $seo_link[ $i ] ) : null,
|
||||
'noindex' => $noindex[ $i ],
|
||||
'copy_from' => $copy_from[ $i ] != '' ? $copy_from[ $i ] : null,
|
||||
'block_direct_access' => $block_direct_access[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_langs', [
|
||||
'article_id' => (int)$article_id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title != '' ? $title : null,
|
||||
'main_image' => $main_image != '' ? $main_image : null,
|
||||
'entry' => $entry != '' ? $entry : null,
|
||||
'text' => $text != '' ? $text : null,
|
||||
'table_of_contents' => $table_of_contents != '' ? $table_of_contents : null,
|
||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
||||
'noindex' => $noindex,
|
||||
'copy_from' => $copy_from != '' ? $copy_from : null,
|
||||
'block_direct_access' => $block_direct_access
|
||||
] );
|
||||
}
|
||||
|
||||
/* dodatkowe parametry */
|
||||
$mdb -> delete( 'pp_articles_additional_values', [ 'article_id' => (int)$article_id ] );
|
||||
|
||||
/* parametry bez wersji językowych */
|
||||
$results = $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $row['id'],
|
||||
'value' => $params[ 'ap_' . $row['name'] ],
|
||||
'article_id' => (int)$article_id,
|
||||
'language_id' => null
|
||||
] );
|
||||
}
|
||||
|
||||
/* parametry z wersjami językowymi */
|
||||
$results = $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 1 ] ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$results2 = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results2 ) ) foreach ( $results2 as $row2 )
|
||||
{
|
||||
$mdb -> insert( 'pp_articles_additional_values', [
|
||||
'param_id' => $row['id'],
|
||||
'value' => $params[ 'ap_' . $row['name'] . '_' . $row2['id'] ],
|
||||
'article_id' => (int)$article_id,
|
||||
'language_id' => $row2['id']
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
/* strony */
|
||||
$not_in = [ 0 ];
|
||||
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
$not_in[] = $page;
|
||||
else if ( $pages )
|
||||
$not_in[] = $pages;
|
||||
|
||||
$mdb -> delete( 'pp_articles_pages', [ 'AND' => [ 'article_id' => (int)$article_id, 'page_id[!]' => $not_in ] ] );
|
||||
|
||||
$pages_tmp = $mdb -> select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$article_id ] );
|
||||
|
||||
if ( !is_array( $pages ) )
|
||||
$pages = [ $pages ];
|
||||
|
||||
$pages = array_diff( $pages, $pages_tmp );
|
||||
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
{
|
||||
$order = self::max_order() + 1;
|
||||
|
||||
$mdb -> insert( 'pp_articles_pages', [
|
||||
'article_id' => (int)$article_id,
|
||||
'page_id' => (int)$page,
|
||||
'o' => (int)$order
|
||||
] );
|
||||
}
|
||||
|
||||
/* pliki */
|
||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_files/article_' . $article_id;
|
||||
|
||||
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$mdb -> update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => (int)$article_id ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$created = false;
|
||||
|
||||
/* zdjęcia */
|
||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$dir = '/upload/article_images/article_' . $article_id;
|
||||
|
||||
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
||||
|
||||
if ( file_exists( '../' . $new_file_name ) )
|
||||
{
|
||||
$ext = strrpos( $new_file_name, '.' );
|
||||
$fileName_a = substr( $new_file_name, 0, $ext );
|
||||
$fileName_b = substr( $new_file_name, $ext );
|
||||
|
||||
$count = 1;
|
||||
|
||||
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
||||
$count++;
|
||||
|
||||
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
||||
}
|
||||
|
||||
if ( file_exists( '..' . $row['src'] ) )
|
||||
{
|
||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||
{
|
||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||
$created = true;
|
||||
}
|
||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||
}
|
||||
|
||||
$mdb -> update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$article_id ], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
|
||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
}
|
||||
|
||||
$mdb -> delete( 'pp_articles_images', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
}
|
||||
|
||||
$mdb -> delete( 'pp_articles_files', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
||||
|
||||
/* tagi */
|
||||
$mdb -> delete( 'pp_articles_tags', [ 'article_id' => (int)$article_id ] );
|
||||
|
||||
$tags = explode( ',', $tags );
|
||||
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
||||
{
|
||||
if ( trim( $tag ) != '' )
|
||||
{
|
||||
$tag_id = $mdb -> get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
||||
if ( !$tag_id )
|
||||
{
|
||||
$mdb -> insert( 'pp_tags', [ 'name' => $tag ] );
|
||||
$tag_id = $mdb -> id();
|
||||
}
|
||||
|
||||
$mdb -> insert( 'pp_articles_tags', [ 'article_id' => (int)$article_id, 'tag_id' => (int)$tag_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
return $article_id;
|
||||
}
|
||||
global $user;
|
||||
return self::repo()->articleSave(
|
||||
$article_id, $title, $main_image, $entry, $text, $table_of_contents, $status, $show_title, $show_table_of_contents, $show_date_add, $date_add, $show_date_modify, $date_modify, $seo_link, $meta_title, $meta_description,
|
||||
$meta_keywords, $layout_id, $pages, $noindex, $repeat_entry, $copy_from, $social_icons, $event_date, $tags, $block_direct_access, $priority,
|
||||
$password, $pixieset, $id_author, $params, (int)$user['id']
|
||||
);
|
||||
}
|
||||
|
||||
public static function delete_nonassigned_files()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
}
|
||||
|
||||
$mdb -> delete( 'pp_articles_files', [ 'article_id' => null ] );
|
||||
self::repo()->deleteNonassignedFiles();
|
||||
}
|
||||
|
||||
public static function delete_nonassigned_images()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( file_exists( '../' . $row['src'] ) )
|
||||
unlink( '../' . $row['src'] );
|
||||
}
|
||||
|
||||
$mdb -> delete( 'pp_articles_images', [ 'article_id' => null ] );
|
||||
self::repo()->deleteNonassignedImages();
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -3,139 +3,36 @@ namespace admin\factory;
|
||||
|
||||
class Layouts
|
||||
{
|
||||
public static function layout_delete( $layout_id )
|
||||
private static function repo(): \Domain\Layouts\LayoutsRepository
|
||||
{
|
||||
global $mdb;
|
||||
if ( $mdb -> count( 'pp_layouts' ) > 1 )
|
||||
return $mdb -> delete( 'pp_layouts', [ 'id' => (int)$layout_id ] );
|
||||
return false;
|
||||
return new \Domain\Layouts\LayoutsRepository( $mdb );
|
||||
}
|
||||
|
||||
public static function layout_delete( $layout_id )
|
||||
{
|
||||
return self::repo()->layoutDelete( $layout_id );
|
||||
}
|
||||
|
||||
public static function layout_details( $layout_id )
|
||||
{
|
||||
global $mdb;
|
||||
$layout = $mdb -> get( 'pp_layouts', '*', [ 'id' => (int)$layout_id ] );
|
||||
|
||||
$layout['pages'] = $mdb -> select( 'pp_layouts_pages', 'page_id', [ 'layout_id' => (int)$layout_id ] );
|
||||
|
||||
return $layout;
|
||||
|
||||
return self::repo()->layoutDetails( $layout_id );
|
||||
}
|
||||
|
||||
public static function layout_save( $layout_id, $name, $status, $pages, $html, $css, $js, $m_html, $m_css, $m_js )
|
||||
{
|
||||
global $mdb;
|
||||
if ( !$layout_id )
|
||||
{
|
||||
if ( $status == 'on' )
|
||||
$mdb -> update( 'pp_layouts', [ 'status' => 0 ] );
|
||||
|
||||
$mdb -> insert( 'pp_layouts', [
|
||||
'name' => $name,
|
||||
'html' => $html,
|
||||
'css' => $css,
|
||||
'js' => $js,
|
||||
'm_html' => $m_html,
|
||||
'm_css' => $m_css,
|
||||
'm_js' => $m_js,
|
||||
'status' => $status == 'on' ? 1 : 0
|
||||
] );
|
||||
|
||||
$id = $mdb -> id();
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
{
|
||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$page ] );
|
||||
|
||||
$mdb -> insert( 'pp_layouts_pages', [
|
||||
'layout_id' => (int)$id,
|
||||
'page_id' => (int)$page
|
||||
] );
|
||||
}
|
||||
else if ( $pages )
|
||||
{
|
||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$pages ] );
|
||||
|
||||
$mdb -> insert( 'pp_layouts_pages', [
|
||||
'layout_id' => (int)$id,
|
||||
'page_id' => (int)$pages
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $status == 'on' )
|
||||
$mdb -> update( 'pp_layouts', [ 'status' => 0 ] );
|
||||
|
||||
$mdb -> update( 'pp_layouts', [
|
||||
'name' => $name,
|
||||
'html' => $html,
|
||||
'css' => $css,
|
||||
'js' => $js,
|
||||
'm_html' => $m_html,
|
||||
'm_css' => $m_css,
|
||||
'm_js' => $m_js,
|
||||
'status' => $status == 'on' ? 1 : 0
|
||||
], [
|
||||
'id' => $layout_id
|
||||
] );
|
||||
|
||||
$mdb -> delete( 'pp_layouts_pages', [ 'layout_id' => (int)$layout_id ] );
|
||||
|
||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||
{
|
||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$page ] );
|
||||
|
||||
$mdb -> insert( 'pp_layouts_pages', [
|
||||
'layout_id' => (int)$layout_id,
|
||||
'page_id' => (int)$page
|
||||
] );
|
||||
}
|
||||
else if ( $pages )
|
||||
{
|
||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$pages ] );
|
||||
|
||||
$mdb -> insert( 'pp_layouts_pages', [
|
||||
'layout_id' => (int)$layout_id,
|
||||
'page_id' => (int)$pages
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $layout_id;
|
||||
}
|
||||
return false;
|
||||
|
||||
{
|
||||
return self::repo()->layoutSave( $layout_id, $name, $status, $pages, $html, $css, $js, $m_html, $m_css, $m_js );
|
||||
}
|
||||
|
||||
public static function menus_list()
|
||||
{
|
||||
global $mdb;
|
||||
$results = $mdb -> select( 'pp_menus', 'id', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$menu = \admin\factory\Pages::menu_details( $row );
|
||||
$menu['pages'] = \admin\factory\Pages::menu_pages( $row );
|
||||
|
||||
$menus[] = $menu;
|
||||
}
|
||||
return $menus;
|
||||
|
||||
return self::repo()->menusList();
|
||||
}
|
||||
|
||||
public static function layouts_list()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_layouts', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
}
|
||||
return self::repo()->layoutsList();
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,120 +1,53 @@
|
||||
<?
|
||||
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
|
||||
class Pages
|
||||
{
|
||||
|
||||
public static $_page_types = [ 0 => 'pełne artykuły', 1 => 'wprowadzenia', 2 => 'miniaturki', 3 => 'link', 4 => 'kontakt' ];
|
||||
public static $_sort_types = [
|
||||
0 => 'data dodania - najstarsze na początku',
|
||||
1 => 'data dodania - najnowsze na początku',
|
||||
2 => 'data modyfikacji - rosnąco',
|
||||
3 => 'data mofyfikacji - malejąco',
|
||||
4 => 'ręczne',
|
||||
5 => 'alfabetycznie - A - Z',
|
||||
6 => 'alfabetycznie - Z - A'
|
||||
0 => 'data dodania - najstarsze na początku',
|
||||
1 => 'data dodania - najnowsze na początku',
|
||||
2 => 'data modyfikacji - rosnąco',
|
||||
3 => 'data mofyfikacji - malejąco',
|
||||
4 => 'ręczne',
|
||||
5 => 'alfabetycznie - A - Z',
|
||||
6 => 'alfabetycznie - Z - A'
|
||||
];
|
||||
|
||||
private static function repo(): \Domain\Pages\PagesRepository
|
||||
{
|
||||
global $mdb;
|
||||
return new \Domain\Pages\PagesRepository( $mdb );
|
||||
}
|
||||
|
||||
public static function save_articles_order( $page_id, $articles )
|
||||
{
|
||||
global $mdb;
|
||||
if ( is_array( $articles ) )
|
||||
{
|
||||
$mdb -> update( 'pp_articles_pages', [ 'o' => 0 ],
|
||||
[ 'page_id' => (int) $page_id ] );
|
||||
|
||||
for ( $i = 0; $i < count( $articles ); $i++ )
|
||||
{
|
||||
if ( $articles[$i]['item_id'] )
|
||||
{
|
||||
$x++;
|
||||
$mdb -> update( 'pp_articles_pages', [ 'o' => $x ],
|
||||
[ 'AND' => [ 'page_id' => (int) $page_id, 'article_id' => $articles[$i]['item_id'] ] ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return self::repo()->saveArticlesOrder( $page_id, $articles );
|
||||
}
|
||||
|
||||
public static function page_articles( $page_id )
|
||||
{
|
||||
global $mdb;
|
||||
$results = $mdb -> query( 'SELECT '
|
||||
. 'article_id, o, status '
|
||||
. 'FROM '
|
||||
. 'pp_articles_pages AS ap '
|
||||
. 'INNER JOIN pp_articles AS a ON a.id = ap.article_id '
|
||||
. 'WHERE '
|
||||
. 'page_id = ' . (int) $page_id . ' AND status != -1 '
|
||||
. 'ORDER BY '
|
||||
. 'o ASC' ) -> fetchAll();
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$row['title'] = \admin\factory\Articles::article_title( $row['article_id'] );
|
||||
$articles[] = $row;
|
||||
}
|
||||
return $articles;
|
||||
return self::repo()->pageArticles( $page_id );
|
||||
}
|
||||
|
||||
public static function menus_list()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_menus', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
return self::repo()->menusList();
|
||||
}
|
||||
|
||||
public static function save_pages_order( $menu_id, $pages )
|
||||
{
|
||||
global $mdb;
|
||||
if ( is_array( $pages ) )
|
||||
{
|
||||
$mdb -> update( 'pp_pages', [ 'o' => 0 ], [ 'menu_id' => (int) $menu_id ] );
|
||||
|
||||
for ( $i = 0; $i < count( $pages ); $i++ )
|
||||
{
|
||||
if ( $pages[$i]['item_id'] )
|
||||
{
|
||||
$pages[$i]['parent_id'] ? $parent_id = $pages[$i]['parent_id'] : $parent_id = 0;
|
||||
|
||||
if ( $pages[$i]['item_id'] && $pages[$i]['depth'] > 1 )
|
||||
{
|
||||
if ( $pages[$i]['depth'] == 2 )
|
||||
$parent_id = null;
|
||||
|
||||
$x++;
|
||||
|
||||
$mdb -> update( 'pp_pages', [ 'o' => $x, 'parent_id' => $parent_id ],
|
||||
[ 'id' => (int) $pages[$i]['item_id'] ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return true;
|
||||
return self::repo()->savePagesOrder( $menu_id, $pages );
|
||||
}
|
||||
|
||||
public static function page_delete( $page_id )
|
||||
{
|
||||
global $mdb;
|
||||
if ( $mdb -> count( 'pp_pages', [ 'parent_id' => (int) $page_id ] ) )
|
||||
return false;
|
||||
|
||||
if ( $mdb -> delete( 'pp_pages', [ 'id' => (int) $page_id ] ) )
|
||||
{
|
||||
\S::delete_cache();
|
||||
\S::htacces();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return self::repo()->pageDelete( $page_id );
|
||||
}
|
||||
|
||||
public static function max_order()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> max( 'pp_pages', 'o' );
|
||||
return self::repo()->maxOrder();
|
||||
}
|
||||
|
||||
public static function page_save(
|
||||
@@ -122,388 +55,70 @@ class Pages
|
||||
$site_title, $block_direct_access, $cache, $canonical
|
||||
)
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$parent_id )
|
||||
$parent_id = null;
|
||||
|
||||
if ( !$page_id )
|
||||
{
|
||||
$order = self::max_order() + 1;
|
||||
|
||||
$mdb -> insert( 'pp_pages', [
|
||||
'menu_id' => (int) $menu_id,
|
||||
'page_type' => $page_type,
|
||||
'sort_type' => $sort_type,
|
||||
'articles_limit' => $articles_limit,
|
||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'o' => (int) $order,
|
||||
'parent_id' => $parent_id,
|
||||
'start' => $start == 'on' ? 1 : 0,
|
||||
'cache' => $cache == 'on' ? 1 : 0
|
||||
] );
|
||||
|
||||
$id = $mdb -> id();
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
if ( $start )
|
||||
$mdb -> update( 'pp_pages', [ 'start' => 0 ], [ 'id[!]' => (int)$id ] );
|
||||
|
||||
if ( $layout_id )
|
||||
$mdb -> insert( 'pp_layouts_pages', [ 'page_id' => (int) $id, 'layout_id' => (int)$layout_id ] );
|
||||
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_pages_langs', [
|
||||
'page_id' => (int) $id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[$i] != '' ? $title[$i] : null,
|
||||
'meta_description' => $meta_description[$i] != '' ? $meta_description[$i] : null,
|
||||
'meta_keywords' => $meta_keywords[$i] != '' ? $meta_keywords[$i] : null,
|
||||
'meta_title' => $meta_title[$i] != '' ? $meta_title[$i] : null,
|
||||
'seo_link' => \S::seo( $seo_link[$i] ) != '' ? \S::seo( $seo_link[$i] ) : null,
|
||||
'noindex' => $noindex[$i],
|
||||
'site_title' => $site_title[$i] != '' ? $site_title[$i] : null,
|
||||
'link' => $link[$i] != '' ? $link[$i] : null,
|
||||
'block_direct_access' => $block_direct_access[$i],
|
||||
'canonical' => $canonical[$i] != '' ? $canonical[$i] : null
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_pages_langs', [
|
||||
'page_id' => (int) $id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title != '' ? $title : null,
|
||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
||||
'noindex' => $noindex,
|
||||
'site_title' => $site_title != '' ? $site_title : null,
|
||||
'link' => $link != '' ? $link : null,
|
||||
'block_direct_access' => $block_direct_access,
|
||||
'canonical' => $canonical != '' ? $canonical : null
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_pages',
|
||||
[
|
||||
'menu_id' => (int) $menu_id,
|
||||
'page_type' => $page_type,
|
||||
'sort_type' => $sort_type,
|
||||
'articles_limit' => $articles_limit,
|
||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'parent_id' => $parent_id,
|
||||
'start' => $start == 'on' ? 1 : 0,
|
||||
'cache' => $cache == 'on' ? 1 : 0
|
||||
], [
|
||||
'id' => (int) $page_id
|
||||
] );
|
||||
|
||||
if ( $layout_id )
|
||||
{
|
||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int) $page_id ] );
|
||||
$mdb -> insert( 'pp_layouts_pages',
|
||||
[ 'layout_id' => (int) $layout_id, 'page_id' => (int) $page_id ] );
|
||||
}
|
||||
|
||||
if ( $start )
|
||||
$mdb -> update( 'pp_pages', [ 'start' => 0 ],
|
||||
[ 'id[!]' => (int) $page_id ] );
|
||||
|
||||
$i = 0;
|
||||
|
||||
$mdb -> delete( 'pp_pages_langs', [ 'page_id' => (int) $page_id ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ],
|
||||
[ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 )
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_pages_langs',
|
||||
[
|
||||
'page_id' => (int) $page_id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[$i] != '' ? $title[$i] : null,
|
||||
'meta_description' => $meta_description[$i] != '' ? $meta_description[$i] : null,
|
||||
'meta_keywords' => $meta_keywords[$i] != '' ? $meta_keywords[$i] : null,
|
||||
'meta_title' => $meta_title[$i] != '' ? $meta_title[$i] : null,
|
||||
'seo_link' => \S::seo( $seo_link[$i] ) != '' ? \S::seo( $seo_link[$i] ) : null,
|
||||
'noindex' => $noindex[$i],
|
||||
'site_title' => $site_title[$i] != '' ? $site_title[$i] : null,
|
||||
'link' => $link[$i] != '' ? $link[$i] : null,
|
||||
'block_direct_access' => $block_direct_access[$i],
|
||||
'canonical' => $canonical[$i] != '' ? $canonical[$i] : null
|
||||
] );
|
||||
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 )
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_pages_langs',
|
||||
[
|
||||
'page_id' => (int) $page_id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title != '' ? $title : null,
|
||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
||||
'noindex' => $noindex,
|
||||
'site_title' => $site_title != '' ? $site_title : null,
|
||||
'link' => $link != '' ? $link : null,
|
||||
'block_direct_access' => $block_direct_access,
|
||||
'canonical' => $canonical != '' ? $canonical : null
|
||||
] );
|
||||
}
|
||||
|
||||
self::update_supages_menu_id( $page_id, $menu_id );
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_cache();
|
||||
|
||||
return $page_id;
|
||||
}
|
||||
return false;
|
||||
return self::repo()->pageSave(
|
||||
$page_id, $title, $seo_link, $meta_title, $meta_description, $meta_keywords, $menu_id, $parent_id, $page_type, $sort_type, $layout_id, $articles_limit, $show_title, $status, $link, $noindex, $start,
|
||||
$site_title, $block_direct_access, $cache, $canonical
|
||||
);
|
||||
}
|
||||
|
||||
public static function update_supages_menu_id( $parent_id, $menu_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$mdb -> update( 'pp_pages', [ 'menu_id' => (int) $menu_id ],
|
||||
[ 'parent_id' => $parent_id ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_pages', [ 'id' ], [ 'parent_id' => $parent_id ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
self::update_supages_menu_id( $row['id'], $menu_id );
|
||||
self::repo()->updateSubpagesMenuId( (int) $parent_id, (int) $menu_id );
|
||||
}
|
||||
|
||||
public static function generate_seo_link( $title, $page_id, $article_id,
|
||||
$lang, $pid )
|
||||
public static function generate_seo_link( $title, $page_id, $article_id, $lang, $pid )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$seo_link = \S::seo( $title );
|
||||
|
||||
|
||||
while ( !$seo_link_check )
|
||||
{
|
||||
if ( $mdb -> count( 'pp_pages_langs',
|
||||
[ 'AND' => [ 'seo_link' => $seo_link, 'page_id[!]' => (int) $page_id ] ] ) )
|
||||
$seo_link = $seo_link . '-' . ( ++$i );
|
||||
else
|
||||
$seo_link_check = true;
|
||||
}
|
||||
|
||||
$seo_link_check = false;
|
||||
|
||||
while ( !$seo_link_check )
|
||||
{
|
||||
if ( $mdb -> count( 'pp_articles_langs',
|
||||
[ 'AND' => [ 'seo_link' => $seo_link, 'article_id[!]' => (int) $article_id ] ] ) )
|
||||
$seo_link = $seo_link . '-' . ( ++$i );
|
||||
else
|
||||
$seo_link_check = true;
|
||||
}
|
||||
return $seo_link;
|
||||
return self::repo()->generateSeoLink( $title, $page_id, $article_id, $lang, $pid );
|
||||
}
|
||||
|
||||
public static function google_url_preview( $page_id, $title, $lang, $pid, $id, $seo_link, $language_link = '' )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$prefix = $language_link;
|
||||
$status = true;
|
||||
$id_page = $page_id;
|
||||
|
||||
do
|
||||
{
|
||||
if ( $page_id )
|
||||
{
|
||||
$parent = \admin\factory\Pages::page_details( $page_id );
|
||||
$parent_id = $parent['parent_id'];
|
||||
}
|
||||
else
|
||||
$parent_id = $pid;
|
||||
|
||||
if ( $parent_id )
|
||||
{
|
||||
$results = $mdb -> query( "SELECT title, seo_link, page_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $parent_id . " AND ppl.lang_id = '" . $lang . "' " ) -> fetchAll();
|
||||
if ( $results[0]['seo_link'] )
|
||||
$seo = $results[0]['seo_link'] . '/' . $seo;
|
||||
else
|
||||
$seo = 's-' . $results[0]['page_id'] . '-' . \S::seo( $results[0]['title'] ) . '/' . $seo;
|
||||
$page_id = $results[0]['page_id'];
|
||||
}
|
||||
else
|
||||
$status = false;
|
||||
}
|
||||
while ( $status );
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
if ( !$seo_link )
|
||||
$seo = $seo . 's-' . $id . '-' . \S::seo( $title );
|
||||
else
|
||||
$seo = $seo . $seo_link;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !$seo_link )
|
||||
$seo = $seo . 's-' . $id_page . '-' . \S::seo( $title );
|
||||
else
|
||||
$seo = $seo . $seo_link;
|
||||
}
|
||||
|
||||
if ( $prefix )
|
||||
$seo = $prefix . $seo;
|
||||
|
||||
return $seo;
|
||||
return self::repo()->googleUrlPreview( $page_id, $title, $lang, $pid, $id, $seo_link, $language_link );
|
||||
}
|
||||
|
||||
public static function menu_delete( $menu_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $mdb -> count( 'pp_pages', [ 'menu_id' => (int) $menu_id ] ) )
|
||||
return false;
|
||||
|
||||
return $mdb -> delete( 'pp_menus', [ 'id' => (int) $menu_id ] );
|
||||
return self::repo()->menuDelete( $menu_id );
|
||||
}
|
||||
|
||||
public static function menu_details( $menu_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_menus', '*', [ 'id' => (int) $menu_id ] );
|
||||
return self::repo()->menuDetails( $menu_id );
|
||||
}
|
||||
|
||||
public static function menu_save( $menu_id, $name, $status )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$status == 'on' ? $status = 1 : $status = 0;
|
||||
|
||||
if ( !$menu_id )
|
||||
{
|
||||
return $mdb -> insert( 'pp_menus',
|
||||
[
|
||||
'name' => $name,
|
||||
'status' => $status
|
||||
] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_menus',
|
||||
[
|
||||
'name' => $name,
|
||||
'status' => $status
|
||||
], [
|
||||
'id' => (int) $menu_id
|
||||
] );
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return self::repo()->menuSave( $menu_id, $name, $status );
|
||||
}
|
||||
|
||||
public static function menu_lists()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_menus', '*', [ 'ORDER' => [ 'id' => 'ASC' ] ] );
|
||||
return self::repo()->menuLists();
|
||||
}
|
||||
|
||||
public static function page_details( $page_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$page = $mdb -> get( 'pp_pages', '*', [ 'id' => (int) $page_id ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_pages_langs', '*',
|
||||
[ 'page_id' => (int) $page_id ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$page['languages'][$row['lang_id']] = $row;
|
||||
|
||||
$page['layout_id'] = $mdb -> get( 'pp_layouts_pages', 'layout_id',
|
||||
[ 'page_id' => (int) $page_id ] );
|
||||
|
||||
return $page;
|
||||
return self::repo()->pageDetails( $page_id );
|
||||
}
|
||||
|
||||
public static function page_url( $page_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> query( "SELECT seo_link, title lang_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $page_id . " AND seo_link != '' ORDER BY o ASC LIMIT 1" ) -> fetchAll();
|
||||
|
||||
if ( !$results[0]['seo_link'] )
|
||||
{
|
||||
$title = self::page_title( $article_id );
|
||||
return 's-' . $page_id . '-' . \S::seo( $title );
|
||||
}
|
||||
else
|
||||
return $results[0]['seo_link'];
|
||||
return self::repo()->pageUrl( $page_id );
|
||||
}
|
||||
|
||||
public static function page_title( $page_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$result = $mdb -> select( 'pp_pages_langs',
|
||||
[ '[><]pp_langs' => [ 'lang_id' => 'id' ] ], 'title',
|
||||
[ 'AND' => [ 'page_id' => (int) $page_id, 'title[!]' => '' ], 'ORDER' => [ 'o' => 'ASC' ], 'LIMIT' => 1 ] );
|
||||
return $result[0];
|
||||
return self::repo()->pageTitle( $page_id );
|
||||
}
|
||||
|
||||
public static function page_languages( $page_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_pages_langs', '*',
|
||||
[ 'AND' => [ 'page_id' => (int) $page_id, 'title[!]' => null ] ] );
|
||||
return self::repo()->pageLanguages( $page_id );
|
||||
}
|
||||
|
||||
public static function menu_pages( $menu_id, $parent_id = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> select( 'pp_pages',
|
||||
[ 'id', 'menu_id', 'status', 'parent_id', 'start' ],
|
||||
[ 'AND' => [ 'menu_id' => $menu_id, 'parent_id' => $parent_id ], 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$row['title'] = self::page_title( $row['id'] );
|
||||
$row['languages'] = self::page_languages( $row['id'] );
|
||||
$row['subpages'] = self::menu_pages( $menu_id, $row['id'] );
|
||||
|
||||
$pages[] = $row;
|
||||
}
|
||||
|
||||
return $pages;
|
||||
return self::repo()->menuPages( $menu_id, $parent_id );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
103
autoload/admin/factory/class.Releases.php
Normal file
103
autoload/admin/factory/class.Releases.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function get_versions(): array
|
||||
{
|
||||
global $mdb;
|
||||
$rows = $mdb->select('pp_update_versions', '*', ['ORDER' => ['version' => 'DESC']]);
|
||||
if (!$rows) return [];
|
||||
foreach ($rows as &$row)
|
||||
$row['zip_exists'] = file_exists('../updates/' . self::zip_dir($row['version']) . '/ver_' . $row['version'] . '.zip');
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public static function promote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'stable', 'promoted_at' => date('Y-m-d H:i:s')],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function demote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'beta', 'promoted_at' => null],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function discover_versions(): int
|
||||
{
|
||||
global $mdb;
|
||||
$known = array_flip($mdb->select('pp_update_versions', 'version', []) ?: []);
|
||||
$zips = glob('../updates/*/ver_*.zip') ?: [];
|
||||
$added = 0;
|
||||
foreach ($zips as $path) {
|
||||
preg_match('/ver_([0-9.]+)\.zip$/', $path, $m);
|
||||
if (!$m) continue;
|
||||
$ver = $m[1];
|
||||
if (isset($known[$ver])) continue;
|
||||
$mdb->insert('pp_update_versions', [
|
||||
'version' => $ver,
|
||||
'channel' => 'beta',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
$known[$ver] = true;
|
||||
$added++;
|
||||
}
|
||||
return $added;
|
||||
}
|
||||
|
||||
public static function get_licenses(): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->select('pp_update_licenses', '*', ['ORDER' => ['domain' => 'ASC']]) ?: [];
|
||||
}
|
||||
|
||||
public static function get_license(int $id): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->get('pp_update_licenses', '*', ['id' => $id]) ?: [];
|
||||
}
|
||||
|
||||
public static function save_license(array $data): void
|
||||
{
|
||||
global $mdb;
|
||||
$row = [
|
||||
'key' => trim($data['key'] ?? ''),
|
||||
'domain' => trim($data['domain'] ?? ''),
|
||||
'valid_to_date' => $data['valid_to_date'] ?: null,
|
||||
'valid_to_version' => $data['valid_to_version'] ?: null,
|
||||
'beta' => (int)(bool)($data['beta'] ?? 0),
|
||||
'note' => trim($data['note'] ?? ''),
|
||||
];
|
||||
if (!empty($data['id']))
|
||||
$mdb->update('pp_update_licenses', $row, ['id' => (int)$data['id']]);
|
||||
else
|
||||
$mdb->insert('pp_update_licenses', $row);
|
||||
}
|
||||
|
||||
public static function delete_license(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->delete('pp_update_licenses', ['id' => $id]);
|
||||
}
|
||||
|
||||
public static function toggle_beta(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$license = $mdb->get('pp_update_licenses', ['id', 'beta'], ['id' => $id]);
|
||||
if ($license)
|
||||
$mdb->update('pp_update_licenses', ['beta' => $license['beta'] ? 0 : 1], ['id' => $id]);
|
||||
}
|
||||
|
||||
private static function zip_dir(string $version): string
|
||||
{
|
||||
return substr($version, 0, strlen($version) - (strlen($version) == 5 ? 2 : 1)) . '0';
|
||||
}
|
||||
}
|
||||
13
autoload/admin/view/class.Releases.php
Normal file
13
autoload/admin/view/class.Releases.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace admin\view;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl->versions = \admin\factory\Releases::get_versions();
|
||||
$tpl->licenses = \admin\factory\Releases::get_licenses();
|
||||
return $tpl->render('releases/main-view');
|
||||
}
|
||||
}
|
||||
10
composer.json
Normal file
10
composer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
1688
composer.lock
generated
Normal file
1688
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
composer.phar
Normal file
BIN
composer.phar
Normal file
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
$database['host'] = 'localhost';
|
||||
$database['host_remote'] = 'host117523.hostido.net.pl';
|
||||
$database['user'] = 'host117523_cmspro';
|
||||
$database['password'] = '3sJADeqKHLqHddfavDeR';
|
||||
$database['name'] = 'host117523_cmspro';
|
||||
|
||||
@@ -57,17 +57,19 @@ autoload/Shared/
|
||||
Stare klasy (`class.S.php`, `class.Cache.php`, itd.) są teraz cienkimi
|
||||
wrapperami — zachowana pełna kompatybilność wsteczna.
|
||||
|
||||
### Faza 2 (w toku) — Domain Repositories (`autoload/Domain/`)
|
||||
### Faza 2 (w toku) - Domain Repositories (`autoload/Domain/`)
|
||||
|
||||
```
|
||||
autoload/Domain/
|
||||
├── Languages/LanguagesRepository.php ← \Domain\Languages\LanguagesRepository ✓
|
||||
├── Settings/SettingsRepository.php ← \Domain\Settings\SettingsRepository ✓
|
||||
└── User/UserRepository.php ← \Domain\User\UserRepository ✓
|
||||
|- Languages/LanguagesRepository.php <- \Domain\Languages\LanguagesRepository OK
|
||||
|- Settings/SettingsRepository.php <- \Domain\Settings\SettingsRepository OK
|
||||
|- User/UserRepository.php <- \Domain\User\UserRepository OK
|
||||
|- Pages/PagesRepository.php <- \Domain\Pages\PagesRepository OK
|
||||
|- Layouts/LayoutsRepository.php <- \Domain\Layouts\LayoutsRepository OK
|
||||
`- Articles/ArticlesRepository.php <- \Domain\Articles\ArticlesRepository OK (w toku)
|
||||
```
|
||||
|
||||
Następne: `Domain\Pages`, `Domain\Layouts`, `Domain\Articles`, ...
|
||||
|
||||
Nastepne: `Domain\Banners`, `Domain\Authors`, `Domain\Newsletter`, ...
|
||||
---
|
||||
|
||||
## Katalogi
|
||||
@@ -118,3 +120,4 @@ Główne tabele: `pp_users`, `pp_articles`, `pp_articles_langs`, `pp_pages`,
|
||||
- Tabela: `pp_languages`
|
||||
- Składnia w treści: `[LANG:klucz]`
|
||||
- Cache tłumaczeń: `$_SESSION['lang-{lang_id}']`
|
||||
|
||||
|
||||
120
docs/TESTING.md
120
docs/TESTING.md
@@ -1,106 +1,54 @@
|
||||
# Testowanie shopPRO
|
||||
# Testowanie cmsPRO
|
||||
|
||||
## Szybki start
|
||||
|
||||
```bash
|
||||
# Pelny suite (PowerShell — rekomendowane)
|
||||
./test.ps1
|
||||
# Instalacja PHPUnit (jednorazowo)
|
||||
composer install
|
||||
|
||||
# Uruchomienie testów
|
||||
./vendor/bin/phpunit
|
||||
|
||||
# Konkretny plik
|
||||
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php
|
||||
./vendor/bin/phpunit tests/Unit/Domain/Settings/SettingsRepositoryTest.php
|
||||
|
||||
# Konkretny test
|
||||
./test.ps1 --filter testGetQuantityReturnsCorrectValue
|
||||
|
||||
# Alternatywne
|
||||
composer test # standard
|
||||
./test.bat # testdox (czytelna lista)
|
||||
./test-simple.bat # kropki
|
||||
./test-debug.bat # debug
|
||||
./test.sh # Git Bash
|
||||
./vendor/bin/phpunit --filter testAllSettingsReturnsMappedArray
|
||||
```
|
||||
|
||||
## Aktualny stan
|
||||
|
||||
```text
|
||||
OK (805 tests, 2253 assertions)
|
||||
Testy jednostkowe dla Domain\ (Faza 2 DDD)
|
||||
```
|
||||
|
||||
Zweryfikowano: 2026-02-24 (ver. 0.318)
|
||||
|
||||
## Konfiguracja
|
||||
|
||||
- **PHPUnit 9.6** via `phpunit.phar`
|
||||
- **PHPUnit 10** via `composer`
|
||||
- **Bootstrap:** `tests/bootstrap.php`
|
||||
- **Config:** `phpunit.xml`
|
||||
|
||||
## Struktura testow
|
||||
## Struktura testów
|
||||
|
||||
```
|
||||
tests/
|
||||
|-- bootstrap.php
|
||||
|-- stubs/
|
||||
| |-- CacheHandler.php (inline w bootstrap)
|
||||
| |-- Helpers.php (Shared\Helpers\Helpers stub)
|
||||
| `-- ShopProduct.php (shop\Product stub)
|
||||
|-- Unit/
|
||||
| |-- Domain/
|
||||
| | |-- Article/ArticleRepositoryTest.php
|
||||
| | |-- Attribute/AttributeRepositoryTest.php
|
||||
| | |-- Banner/BannerRepositoryTest.php
|
||||
| | |-- Basket/BasketCalculatorTest.php
|
||||
| | |-- Cache/CacheRepositoryTest.php
|
||||
| | |-- Category/CategoryRepositoryTest.php
|
||||
| | |-- Coupon/CouponRepositoryTest.php
|
||||
| | |-- CronJob/CronJobTypeTest.php
|
||||
| | |-- CronJob/CronJobRepositoryTest.php
|
||||
| | |-- CronJob/CronJobProcessorTest.php
|
||||
| | |-- Dictionaries/DictionariesRepositoryTest.php
|
||||
| | |-- Integrations/IntegrationsRepositoryTest.php
|
||||
| | |-- Languages/LanguagesRepositoryTest.php
|
||||
| | |-- Layouts/LayoutsRepositoryTest.php
|
||||
| | |-- Newsletter/NewsletterRepositoryTest.php
|
||||
| | |-- Pages/PagesRepositoryTest.php
|
||||
| | |-- PaymentMethod/PaymentMethodRepositoryTest.php
|
||||
| | |-- Producer/ProducerRepositoryTest.php
|
||||
| | |-- Product/ProductRepositoryTest.php
|
||||
| | |-- ProductSet/ProductSetRepositoryTest.php
|
||||
| | |-- Promotion/PromotionRepositoryTest.php
|
||||
| | |-- Settings/SettingsRepositoryTest.php
|
||||
| | |-- ShopStatus/ShopStatusRepositoryTest.php
|
||||
| | |-- Transport/TransportRepositoryTest.php
|
||||
| | |-- Update/UpdateRepositoryTest.php
|
||||
| | `-- User/UserRepositoryTest.php
|
||||
| `-- admin/
|
||||
| `-- Controllers/
|
||||
| |-- ArticlesControllerTest.php
|
||||
| |-- DictionariesControllerTest.php
|
||||
| |-- IntegrationsControllerTest.php
|
||||
| |-- ProductArchiveControllerTest.php
|
||||
| |-- SettingsControllerTest.php
|
||||
| |-- ShopAttributeControllerTest.php
|
||||
| |-- ShopCategoryControllerTest.php
|
||||
| |-- ShopCouponControllerTest.php
|
||||
| |-- ShopPaymentMethodControllerTest.php
|
||||
| |-- ShopProducerControllerTest.php
|
||||
| |-- ShopProductControllerTest.php
|
||||
| |-- ShopProductSetsControllerTest.php
|
||||
| |-- ShopPromotionControllerTest.php
|
||||
| |-- ShopStatusesControllerTest.php
|
||||
| |-- ShopTransportControllerTest.php
|
||||
| `-- UsersControllerTest.php
|
||||
| `-- api/
|
||||
| |-- ApiRouterTest.php
|
||||
| `-- Controllers/
|
||||
| |-- OrdersApiControllerTest.php
|
||||
| |-- ProductsApiControllerTest.php
|
||||
| `-- DictionariesApiControllerTest.php
|
||||
`-- Integration/ (puste — zarezerwowane)
|
||||
├── bootstrap.php ← autoloader + stuby (CacheHandler, S)
|
||||
└── Unit/
|
||||
└── Domain/
|
||||
├── Languages/LanguagesRepositoryTest.php
|
||||
├── Settings/SettingsRepositoryTest.php
|
||||
└── User/UserRepositoryTest.php
|
||||
```
|
||||
|
||||
## Dodawanie nowych testow
|
||||
## Stuby (bootstrap.php)
|
||||
|
||||
1. Plik w `tests/Unit/Domain/<Module>/<Class>Test.php`, `tests/Unit/admin/Controllers/<Class>Test.php` lub `tests/Unit/api/Controllers/<Class>Test.php`.
|
||||
- `\Shared\Cache\CacheHandler` — in-memory stub z `fetch()`/`store()`/`delete()`/`reset()`
|
||||
- `\S` — stub z `delete_cache()`, `htacces()`, `get_domain()`, `send_email()`
|
||||
- `medoo` — mockowany przez PHPUnit (`$this->createMock(\medoo::class)`)
|
||||
|
||||
## Dodawanie nowych testów
|
||||
|
||||
1. Plik w `tests/Unit/Domain/<Modul>/<Klasa>Test.php`.
|
||||
2. Rozszerz `PHPUnit\Framework\TestCase`.
|
||||
3. Nazwy metod zaczynaj od `test`.
|
||||
4. Wzorzec AAA: Arrange, Act, Assert.
|
||||
@@ -108,19 +56,11 @@ tests/
|
||||
## Mockowanie Medoo
|
||||
|
||||
```php
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')->willReturn(42);
|
||||
$db = $this->createMock(\medoo::class);
|
||||
$db->method('get')->willReturn(['id' => 1]);
|
||||
|
||||
$repo = new ProductRepository($mockDb);
|
||||
$value = $repo->getQuantity(123);
|
||||
$repo = new SettingsRepository($db);
|
||||
$value = $repo->visitCounter();
|
||||
|
||||
$this->assertEquals(42, $value);
|
||||
$this->assertSame('1', $value);
|
||||
```
|
||||
|
||||
## Bootstrap — stuby
|
||||
|
||||
`tests/bootstrap.php` rejestruje autoloader i definiuje stuby:
|
||||
- `Redis`, `RedisConnection` — klasy Redis (aby nie wymagac rozszerzenia)
|
||||
- `Shared\Cache\CacheHandler` — inline stub z `get()`/`set()`/`exists()`/`delete()`/`deletePattern()`
|
||||
- `Shared\Helpers\Helpers` — z `tests/stubs/Helpers.php`
|
||||
- `shop\Product` — z `tests/stubs/ShopProduct.php`
|
||||
|
||||
113
docs/plans/2026-02-28-update-channels-design.md
Normal file
113
docs/plans/2026-02-28-update-channels-design.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Design: Dwukanałowy system aktualizacji + zarządzanie licencjami
|
||||
|
||||
**Data:** 2026-02-28
|
||||
**Status:** Zatwierdzony
|
||||
|
||||
## Kontekst
|
||||
|
||||
cmsPRO jest używany przez wielu klientów. Celem jest wprowadzenie dwustopniowej dystrybucji aktualizacji:
|
||||
- Kanał **beta** — tylko wybrane testowe instalacje (1–2 strony dewelopera)
|
||||
- Kanał **stable** — wszyscy pozostali klienci
|
||||
|
||||
Zarządzanie odbywa się z panelu admina na `cmspro.project-dc.pl` (ten sam serwer i DB co serwer aktualizacji).
|
||||
|
||||
**Kluczowe ograniczenie:** nowy moduł admina oraz nowe tabele DB nigdy nie trafiają do paczek aktualizacyjnych wysyłanych klientom.
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 1: Baza danych
|
||||
|
||||
Dwie nowe tabele tworzone **ręcznie tylko na serwerze dewelopera**. Nie mogą pojawić się w żadnym SQL migracyjnym dla klientów.
|
||||
|
||||
```sql
|
||||
CREATE TABLE pp_update_licenses (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`key` VARCHAR(64) NOT NULL UNIQUE,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
valid_to_date DATE NULL,
|
||||
valid_to_version VARCHAR(10) NULL,
|
||||
beta TINYINT(1) NOT NULL DEFAULT 0,
|
||||
note TEXT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE pp_update_versions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
version VARCHAR(10) NOT NULL UNIQUE,
|
||||
channel ENUM('beta','stable') NOT NULL DEFAULT 'beta',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
promoted_at DATETIME NULL
|
||||
);
|
||||
```
|
||||
|
||||
Dane z `updates/versions.php` (tablica `$license`) migrujemy jednorazowo do `pp_update_licenses`.
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 2: Plik `updates/versions.php`
|
||||
|
||||
Plik zostaje w tym samym miejscu, zmienia się tylko logika (hardkodowane tablice zastępowane odczytem z DB).
|
||||
|
||||
### Nowy przepływ:
|
||||
|
||||
1. `require_once '../config.php'` → dostęp do `$mdb` (Medoo)
|
||||
2. Skan filesystem — lista istniejących ZIPów (jak dotychczas)
|
||||
3. **Auto-discovery:** każdy ZIP nieobecny w `pp_update_versions` → `INSERT IGNORE` z `channel='beta'`. Wrzucenie nowego ZIPa na serwer automatycznie rejestruje go jako beta — bez zmian w `build-update.ps1`.
|
||||
4. Walidacja klucza z `pp_update_licenses` (zamiast `$license[...]`)
|
||||
5. Filtrowanie wersji według kanału:
|
||||
- klient z `beta=1` → wersje gdzie `channel IN ('beta','stable')`
|
||||
- klient z `beta=0` → tylko `channel='stable'`
|
||||
6. Dalsze filtrowanie po `valid_to_date` i `valid_to_version` (bez zmian)
|
||||
7. Wypisanie listy wersji (jak dotychczas)
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 3: Nowy moduł admina `Releases`
|
||||
|
||||
Nowa nazwa `Releases` (odróżnienie od istniejącego modułu `Update` używanego przez klientów do aktualizacji własnej instalacji).
|
||||
|
||||
### Zakładka "Wersje"
|
||||
|
||||
- Tabela z `pp_update_versions` + status istnienia ZIPa na dysku
|
||||
- Kolumny: wersja, kanał, data dodania, data promocji
|
||||
- Akcje: **Promuj do stable** / **Cofnij do beta**
|
||||
|
||||
### Zakładka "Licencje"
|
||||
|
||||
- Tabela z `pp_update_licenses`
|
||||
- Kolumny: domena, klucz (skrócony hash), ważna do daty, ważna do wersji, beta (toggle), notatka
|
||||
- Pełny CRUD: dodaj / edytuj / usuń
|
||||
- Szybki toggle flagi `beta` bezpośrednio z listy
|
||||
|
||||
### Pliki modułu
|
||||
|
||||
Wszystkie wykluczone z paczek ZIP dla klientów:
|
||||
|
||||
```
|
||||
autoload/admin/controls/class.Releases.php
|
||||
autoload/admin/factory/class.Releases.php
|
||||
autoload/admin/view/class.Releases.php
|
||||
admin/templates/releases/main-view.php
|
||||
admin/templates/releases/licenses-view.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 4: Wykluczenie z aktualizacji klientów
|
||||
|
||||
### Pliki PHP
|
||||
W `build-update.ps1` — dodanie powyższych ścieżek do listy wykluczeń.
|
||||
|
||||
### Tabele DB
|
||||
`pp_update_licenses` i `pp_update_versions` **nigdy** nie pojawiają się w:
|
||||
- plikach `_sql.txt` dołączanych do paczek
|
||||
- sekcji `sql` w plikach `_manifest.json`
|
||||
|
||||
Tabele tworzone jednorazowo ręcznie na serwerze dewelopera.
|
||||
|
||||
---
|
||||
|
||||
## Migracja danych
|
||||
|
||||
Jednorazowy skrypt PHP (uruchamiany ręcznie na serwerze) przenosi dane z `$license[...]` z `versions.php` do `pp_update_licenses`. Po migracji tablica `$license` w `versions.php` jest usuwana.
|
||||
766
docs/plans/2026-02-28-update-channels.md
Normal file
766
docs/plans/2026-02-28-update-channels.md
Normal file
@@ -0,0 +1,766 @@
|
||||
# Releases Module Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Wdrożyć dwukanałowy system aktualizacji (beta/stable) z zarządzaniem licencjami w panelu admina.
|
||||
|
||||
**Architecture:** Dwie nowe tabele MySQL (`pp_update_licenses`, `pp_update_versions`) dostępne tylko na serwerze dewelopera. `updates/versions.php` czyta z DB przez Medoo. Nowy moduł `Releases` w panelu admina zarządza wersjami i licencjami. Całość wykluczona z paczek klientów przez `.updateignore`.
|
||||
|
||||
**Tech Stack:** PHP 8.x, Medoo 1.x (`$mdb` global), jQuery UI (dialog), Bootstrap 3 (klasy CSS), `\Tpl` (admin templates z `admin/templates/`), `\S::get()` (POST→GET params).
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Dodaj wykluczenia do `.updateignore`
|
||||
|
||||
**Files:**
|
||||
- Modify: `.updateignore`
|
||||
|
||||
**Step 1: Dopisz nowe wykluczenia**
|
||||
|
||||
Otwórz `.updateignore` i dopisz na końcu:
|
||||
|
||||
```
|
||||
# Moduł zarządzania releaseami (tylko serwer dewelopera)
|
||||
autoload/admin/controls/class.Releases.php
|
||||
autoload/admin/factory/class.Releases.php
|
||||
autoload/admin/view/class.Releases.php
|
||||
admin/templates/releases/
|
||||
|
||||
# Menu dewelopera
|
||||
templates/additional-menu.php
|
||||
```
|
||||
|
||||
**Step 2: Zweryfikuj manualnie**
|
||||
|
||||
Uruchom build w trybie dry-run i sprawdź, że powyższe pliki NIE pojawiają się na liście:
|
||||
```powershell
|
||||
./build-update.ps1 -ToTag v9.999 -DryRun
|
||||
```
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add .updateignore
|
||||
git commit -m "chore: wyklucz modul Releases i menu dewelopera z paczek klientow"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Utwórz tabele DB (jednorazowo na serwerze)
|
||||
|
||||
**Files:**
|
||||
- Create: `_db_releases_setup.sql` (uruchom raz w phpMyAdmin, nie commituj)
|
||||
|
||||
**Step 1: Utwórz plik SQL**
|
||||
|
||||
```sql
|
||||
CREATE TABLE pp_update_licenses (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`key` VARCHAR(64) NOT NULL UNIQUE,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
valid_to_date DATE NULL,
|
||||
valid_to_version VARCHAR(10) NULL,
|
||||
beta TINYINT(1) NOT NULL DEFAULT 0,
|
||||
note TEXT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE pp_update_versions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
version VARCHAR(10) NOT NULL UNIQUE,
|
||||
channel ENUM('beta','stable') NOT NULL DEFAULT 'beta',
|
||||
created_at DATETIME NULL,
|
||||
promoted_at DATETIME NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
```
|
||||
|
||||
**Step 2: Uruchom w phpMyAdmin na serwerze `cmspro.project-dc.pl`**
|
||||
|
||||
Wklej SQL do phpMyAdmin → Execute. Obie tabele muszą być widoczne.
|
||||
|
||||
**Step 3: Nie commituj** — ten plik jest tylko pomocniczy, możesz go usunąć po wykonaniu.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Utwórz `admin\factory\Releases`
|
||||
|
||||
**Files:**
|
||||
- Create: `autoload/admin/factory/class.Releases.php`
|
||||
|
||||
**Step 1: Napisz klasę**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function get_versions(): array
|
||||
{
|
||||
global $mdb;
|
||||
$rows = $mdb->select('pp_update_versions', '*', ['ORDER' => ['version' => 'DESC']]);
|
||||
if (!$rows) return [];
|
||||
foreach ($rows as &$row)
|
||||
$row['zip_exists'] = file_exists('../updates/' . self::zip_dir($row['version']) . '/ver_' . $row['version'] . '.zip');
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public static function promote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'stable', 'promoted_at' => date('Y-m-d H:i:s')],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function demote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'beta', 'promoted_at' => null],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_licenses(): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->select('pp_update_licenses', '*', ['ORDER' => ['domain' => 'ASC']]) ?: [];
|
||||
}
|
||||
|
||||
public static function get_license(int $id): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->get('pp_update_licenses', '*', ['id' => $id]) ?: [];
|
||||
}
|
||||
|
||||
public static function save_license(array $data): void
|
||||
{
|
||||
global $mdb;
|
||||
$row = [
|
||||
'key' => trim($data['key'] ?? ''),
|
||||
'domain' => trim($data['domain'] ?? ''),
|
||||
'valid_to_date' => $data['valid_to_date'] ?: null,
|
||||
'valid_to_version' => $data['valid_to_version'] ?: null,
|
||||
'beta' => (int)(bool)($data['beta'] ?? 0),
|
||||
'note' => trim($data['note'] ?? ''),
|
||||
];
|
||||
if (!empty($data['id']))
|
||||
$mdb->update('pp_update_licenses', $row, ['id' => (int)$data['id']]);
|
||||
else
|
||||
$mdb->insert('pp_update_licenses', $row);
|
||||
}
|
||||
|
||||
public static function delete_license(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->delete('pp_update_licenses', ['id' => $id]);
|
||||
}
|
||||
|
||||
public static function toggle_beta(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$license = $mdb->get('pp_update_licenses', ['id', 'beta'], ['id' => $id]);
|
||||
if ($license)
|
||||
$mdb->update('pp_update_licenses', ['beta' => $license['beta'] ? 0 : 1], ['id' => $id]);
|
||||
}
|
||||
|
||||
private static function zip_dir(string $version): string
|
||||
{
|
||||
return substr($version, 0, strlen($version) - (strlen($version) == 5 ? 2 : 1)) . '0';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l autoload/admin/factory/class.Releases.php
|
||||
```
|
||||
Oczekiwane: `No syntax errors detected`
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Utwórz `admin\controls\Releases`
|
||||
|
||||
**Files:**
|
||||
- Create: `autoload/admin/controls/class.Releases.php`
|
||||
|
||||
**Step 1: Napisz klasę**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
return \admin\view\Releases::main_view();
|
||||
}
|
||||
|
||||
public static function promote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::promote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function demote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::demote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function save_license(): void
|
||||
{
|
||||
\admin\factory\Releases::save_license($_POST);
|
||||
\S::set_message('Licencja została zapisana.');
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function delete_license(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::delete_license($id);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function toggle_beta(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::toggle_beta($id);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l autoload/admin/controls/class.Releases.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Utwórz `admin\view\Releases`
|
||||
|
||||
**Files:**
|
||||
- Create: `autoload/admin/view/class.Releases.php`
|
||||
|
||||
**Step 1: Napisz klasę**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace admin\view;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl->versions = \admin\factory\Releases::get_versions();
|
||||
$tpl->licenses = \admin\factory\Releases::get_licenses();
|
||||
return $tpl->render('releases/main-view');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l autoload/admin/view/class.Releases.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Utwórz szablon `admin/templates/releases/main-view.php`
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/templates/releases/main-view.php`
|
||||
|
||||
**Step 1: Utwórz katalog i plik**
|
||||
|
||||
```php
|
||||
<?php global $user; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
<!-- TABS NAV -->
|
||||
<ul class="nav nav-tabs releases-tabs" style="margin-bottom:20px">
|
||||
<li class="active"><a href="#" data-tab="versions">Wersje</a></li>
|
||||
<li><a href="#" data-tab="licenses">Licencje</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- TAB: WERSJE -->
|
||||
<div id="tab-versions">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h3 class="panel-title">Wersje</h3></div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Wersja</th><th>Kanał</th><th>Dodana</th>
|
||||
<th>Promocja do stable</th><th>ZIP</th><th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<? foreach ($this->versions as $v): ?>
|
||||
<tr>
|
||||
<td><strong><?= htmlspecialchars($v['version']) ?></strong></td>
|
||||
<td>
|
||||
<? if ($v['channel'] == 'stable'): ?>
|
||||
<span class="label label-success">stable</span>
|
||||
<? else: ?>
|
||||
<span class="label label-warning">beta</span>
|
||||
<? endif; ?>
|
||||
</td>
|
||||
<td><?= $v['created_at'] ? substr($v['created_at'], 0, 10) : '-' ?></td>
|
||||
<td><?= $v['promoted_at'] ? substr($v['promoted_at'], 0, 10) : '-' ?></td>
|
||||
<td><?= $v['zip_exists'] ? '<span class="text-success">✓</span>' : '<span class="text-danger">✗</span>' ?></td>
|
||||
<td>
|
||||
<? if ($v['channel'] == 'beta'): ?>
|
||||
<a href="/admin/releases/promote/?version=<?= urlencode($v['version']) ?>"
|
||||
class="btn btn-xs btn-success"
|
||||
onclick="return confirm('Promować <?= $v['version'] ?> do stable?')">
|
||||
Promuj →stable
|
||||
</a>
|
||||
<? else: ?>
|
||||
<a href="/admin/releases/demote/?version=<?= urlencode($v['version']) ?>"
|
||||
class="btn btn-xs btn-default"
|
||||
onclick="return confirm('Cofnąć <?= $v['version'] ?> do beta?')">
|
||||
Cofnij →beta
|
||||
</a>
|
||||
<? endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; ?>
|
||||
<? if (!$this->versions): ?>
|
||||
<tr><td colspan="6" class="text-center text-muted">Brak wersji w bazie. Wersje będą rejestrowane automatycznie przy pierwszym odpytaniu versions.php.</td></tr>
|
||||
<? endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab-versions -->
|
||||
|
||||
<!-- TAB: LICENCJE -->
|
||||
<div id="tab-licenses" style="display:none">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center">
|
||||
<h3 class="panel-title">Licencje</h3>
|
||||
<button class="btn btn-sm btn-system" id="btn-add-license">+ Dodaj licencję</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<!-- Formularz dodawania/edycji (ukryty) -->
|
||||
<div id="license-form-box" style="display:none;border:1px solid #ddd;padding:15px;margin-bottom:20px;background:#f9f9f9">
|
||||
<h4 id="license-form-title">Nowa licencja</h4>
|
||||
<form method="post" action="/admin/releases/save_license/">
|
||||
<input type="hidden" name="id" id="f-id" value="">
|
||||
<div class="row">
|
||||
<div class="form-group col-lg-3">
|
||||
<label>Klucz (hash)</label>
|
||||
<input type="text" name="key" id="f-key" class="form-control" placeholder="md5/hash lub pusty dla domyślnego">
|
||||
</div>
|
||||
<div class="form-group col-lg-3">
|
||||
<label>Domena</label>
|
||||
<input type="text" name="domain" id="f-domain" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group col-lg-2">
|
||||
<label>Ważna do daty</label>
|
||||
<input type="date" name="valid_to_date" id="f-valid-date" class="form-control">
|
||||
</div>
|
||||
<div class="form-group col-lg-2">
|
||||
<label>Ważna do wersji</label>
|
||||
<input type="text" name="valid_to_version" id="f-valid-ver" class="form-control" placeholder="np. 1.618">
|
||||
</div>
|
||||
<div class="form-group col-lg-1">
|
||||
<label>Beta</label><br>
|
||||
<input type="checkbox" name="beta" id="f-beta" value="1">
|
||||
</div>
|
||||
<div class="form-group col-lg-2">
|
||||
<label>Notatka</label>
|
||||
<input type="text" name="note" id="f-note" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-system btn-sm">Zapisz</button>
|
||||
<button type="button" class="btn btn-default btn-sm" id="btn-cancel-license">Anuluj</button>
|
||||
</form>
|
||||
</div><!-- /license-form-box -->
|
||||
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domena</th><th>Klucz</th><th>Do daty</th>
|
||||
<th>Do wersji</th><th>Beta</th><th>Notatka</th><th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<? foreach ($this->licenses as $lic): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($lic['domain']) ?></td>
|
||||
<td><code title="<?= htmlspecialchars($lic['key']) ?>">
|
||||
<?= $lic['key'] === '' ? '<em>(domyślny)</em>' : htmlspecialchars(substr($lic['key'], 0, 8)) . '…' ?>
|
||||
</code></td>
|
||||
<td><?= $lic['valid_to_date'] ?: '<em>∞</em>' ?></td>
|
||||
<td><?= $lic['valid_to_version'] ?: '<em>∞</em>' ?></td>
|
||||
<td>
|
||||
<a href="/admin/releases/toggle_beta/?id=<?= $lic['id'] ?>"
|
||||
class="label <?= $lic['beta'] ? 'label-info' : 'label-default' ?>"
|
||||
title="Kliknij aby przełączyć">
|
||||
<?= $lic['beta'] ? 'beta' : 'stable' ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($lic['note'] ?? '') ?></td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default btn-edit-license"
|
||||
data-id="<?= $lic['id'] ?>"
|
||||
data-key="<?= htmlspecialchars($lic['key']) ?>"
|
||||
data-domain="<?= htmlspecialchars($lic['domain']) ?>"
|
||||
data-valid-date="<?= $lic['valid_to_date'] ?? '' ?>"
|
||||
data-valid-ver="<?= $lic['valid_to_version'] ?? '' ?>"
|
||||
data-beta="<?= $lic['beta'] ?>"
|
||||
data-note="<?= htmlspecialchars($lic['note'] ?? '') ?>">Edytuj</button>
|
||||
<a href="/admin/releases/delete_license/?id=<?= $lic['id'] ?>"
|
||||
class="btn btn-xs btn-danger"
|
||||
onclick="return confirm('Usunąć licencję <?= htmlspecialchars($lic['domain']) ?>?')">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; ?>
|
||||
<? if (!$this->licenses): ?>
|
||||
<tr><td colspan="7" class="text-center text-muted">Brak licencji. Dodaj pierwszą lub uruchom skrypt migracji.</td></tr>
|
||||
<? endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab-licenses -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Przełączanie tabów
|
||||
$('.releases-tabs a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('.releases-tabs li').removeClass('active');
|
||||
$(this).parent().addClass('active');
|
||||
$('[id^="tab-"]').hide();
|
||||
$('#tab-' + $(this).data('tab')).show();
|
||||
});
|
||||
|
||||
// Formularz: Dodaj nową licencję
|
||||
$('#btn-add-license').on('click', function() {
|
||||
$('#license-form-title').text('Nowa licencja');
|
||||
$('#f-id').val('');
|
||||
$('#f-key, #f-domain, #f-valid-date, #f-valid-ver, #f-note').val('');
|
||||
$('#f-beta').prop('checked', false);
|
||||
$('#license-form-box').slideDown();
|
||||
});
|
||||
|
||||
// Formularz: Edytuj istniejącą licencję
|
||||
$('.btn-edit-license').on('click', function() {
|
||||
var d = $(this).data();
|
||||
$('#license-form-title').text('Edytuj licencję: ' + d.domain);
|
||||
$('#f-id').val(d.id);
|
||||
$('#f-key').val(d.key);
|
||||
$('#f-domain').val(d.domain);
|
||||
$('#f-valid-date').val(d.validDate);
|
||||
$('#f-valid-ver').val(d.validVer);
|
||||
$('#f-beta').prop('checked', d.beta == 1);
|
||||
$('#f-note').val(d.note);
|
||||
$('#license-form-box').slideDown();
|
||||
$('html, body').animate({ scrollTop: $('#license-form-box').offset().top - 20 }, 300);
|
||||
});
|
||||
|
||||
// Anuluj formularz
|
||||
$('#btn-cancel-license').on('click', function() {
|
||||
$('#license-form-box').slideUp();
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
**Step 2: Zweryfikuj, że szablon jest poprawny**
|
||||
|
||||
Otwórz w przeglądarce: `https://cmspro.project-dc.pl/admin/releases/main_view/`
|
||||
|
||||
Oczekiwane: strona ładuje się bez błędów PHP, widoczne dwa taby, tabela wersji pusta lub z danymi.
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Dodaj pozycję menu dla dewelopera
|
||||
|
||||
**Files:**
|
||||
- Create: `templates/additional-menu.php` (ten plik nie trafia do klientów — dodany do `.updateignore` w Task 1)
|
||||
|
||||
**Step 1: Utwórz plik**
|
||||
|
||||
```php
|
||||
<?php
|
||||
// Menu tylko na serwerze dewelopera — wykluczone z .updateignore
|
||||
?>
|
||||
<div class="title">Developer</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/releases/main_view/">
|
||||
<img src="/admin/css/icons/settings-20-filled.svg">Releases & Licencje
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Step 2: Sprawdź w przeglądarce**
|
||||
|
||||
Po odświeżeniu panelu admina powinna pojawić się sekcja "Developer" z linkiem "Releases & Licencje" w menu bocznym.
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Migracja danych licencji do DB
|
||||
|
||||
Licencje z hardkodowanej tablicy `$license` w `updates/versions.php` muszą trafić do `pp_update_licenses` **przed** przejściem na nową wersję `versions.php`.
|
||||
|
||||
**Files:**
|
||||
- Create: `_migrate_licenses.php` (tymczasowy, uruchom raz przez przeglądarkę, potem usuń)
|
||||
|
||||
**Step 1: Utwórz skrypt migracji**
|
||||
|
||||
```php
|
||||
<?php
|
||||
// UWAGA: Uruchom JEDEN raz przez przeglądarkę, potem natychmiast usuń!
|
||||
// URL: https://cmspro.project-dc.pl/_migrate_licenses.php
|
||||
|
||||
require_once 'config.php';
|
||||
require_once 'libraries/medoo/medoo.php';
|
||||
|
||||
$mdb = new medoo([
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
]);
|
||||
|
||||
// Wyodrębnij $license z pliku versions.php bez uruchamiania die()
|
||||
$source = file_get_contents('updates/versions.php');
|
||||
|
||||
// Wyciągnij wszystkie wpisy $license['key']['domain'] itd.
|
||||
preg_match_all('/\$license\[\'([^\']*)\'\]\[\'domain\'\]\s*=\s*\'([^\']*)\';/', $source, $m_domain);
|
||||
preg_match_all('/\$license\[\'([^\']*)\'\]\[\'valid_to_date\'\]\s*=\s*\'([^\']*)\';/', $source, $m_date);
|
||||
preg_match_all('/\$license\[\'([^\']*)\'\]\[\'valid_to_version\'\]\s*=\s*\'([^\']*)\';/', $source, $m_ver);
|
||||
|
||||
// Zbuduj mapę po kluczu
|
||||
$dates = array_combine($m_date[1], $m_date[2]);
|
||||
$vers = array_combine($m_ver[1], $m_ver[2]);
|
||||
|
||||
$count = 0;
|
||||
foreach ($m_domain[1] as $i => $key) {
|
||||
$domain = $m_domain[2][$i];
|
||||
$row = [
|
||||
'key' => $key,
|
||||
'domain' => $domain,
|
||||
'valid_to_date' => ($dates[$key] ?? '') ?: null,
|
||||
'valid_to_version' => ($vers[$key] ?? '') ?: null,
|
||||
'beta' => 0,
|
||||
];
|
||||
// Pomiń jeśli już istnieje
|
||||
if ($mdb->has('pp_update_licenses', ['key' => $key])) {
|
||||
echo "SKIP (już istnieje): $domain ($key)<br>";
|
||||
continue;
|
||||
}
|
||||
$mdb->insert('pp_update_licenses', $row);
|
||||
echo "OK: $domain ($key)<br>";
|
||||
$count++;
|
||||
}
|
||||
echo "<hr><strong>Zmigrowano $count licencji.</strong>";
|
||||
echo "<br><strong style='color:red'>USUŃ ten plik z serwera!</strong>";
|
||||
```
|
||||
|
||||
**Step 2: Wgraj na serwer i uruchom**
|
||||
|
||||
```
|
||||
https://cmspro.project-dc.pl/_migrate_licenses.php
|
||||
```
|
||||
|
||||
Oczekiwane: lista "OK: domena (klucz)" dla każdej licencji, na końcu podsumowanie.
|
||||
|
||||
**Step 3: Usuń skrypt z serwera**
|
||||
|
||||
Skasuj `_migrate_licenses.php` — nie commituj go do repozytorium.
|
||||
|
||||
**Step 4: Ustaw flagę beta dla swoich testowych stron**
|
||||
|
||||
W panelu admina `/admin/releases/main_view/` → zakładka Licencje → przy swoich testowych domenach kliknij "stable" aby przełączyć na "beta".
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Przebuduj `updates/versions.php`
|
||||
|
||||
**Files:**
|
||||
- Modify: `updates/versions.php`
|
||||
|
||||
> **WAŻNE:** Wykonaj ten krok dopiero po Task 8 (migracja danych do DB).
|
||||
|
||||
**Step 1: Zastąp całą zawartość pliku**
|
||||
|
||||
```php
|
||||
<?
|
||||
require_once '../config.php';
|
||||
require_once '../libraries/medoo/medoo.php';
|
||||
|
||||
$mdb = new medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$current_ver = 1691; // aktualizowane automatycznie przez build-update.ps1
|
||||
|
||||
// 1. Skan filesystem — lista istniejących ZIPów
|
||||
$versions = [];
|
||||
for ( $i = 1; $i <= $current_ver; $i++ )
|
||||
{
|
||||
$dir = substr( number_format( $i / 1000, 3 ), 0, strlen( number_format( $i / 1000, 3 ) ) - 2 ) . '0';
|
||||
$version_old = number_format( $i / 1000, 2 );
|
||||
$version_new = number_format( $i / 1000, 3 );
|
||||
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_old . '.zip' ) )
|
||||
$versions[] = $version_old;
|
||||
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_new . '.zip' ) )
|
||||
$versions[] = $version_new;
|
||||
}
|
||||
$versions = array_unique( $versions );
|
||||
|
||||
// 2. Walidacja klucza licencji
|
||||
$license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? '' ) ] );
|
||||
if ( !$license )
|
||||
die();
|
||||
|
||||
// 3. Sprawdź ważność daty
|
||||
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
||||
die();
|
||||
|
||||
// 4. Auto-discovery: rejestruj nowe ZIPy jako beta
|
||||
$known = array_flip( $mdb->select( 'pp_update_versions', 'version', [] ) ?: [] );
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
if ( !isset( $known[$ver] ) )
|
||||
{
|
||||
@$mdb->insert( 'pp_update_versions', [
|
||||
'version' => $ver,
|
||||
'channel' => 'beta',
|
||||
'created_at' => date( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
$known[$ver] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Filtruj wersje wg kanału (beta widzi beta+stable, reszta tylko stable)
|
||||
$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ];
|
||||
$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] );
|
||||
|
||||
// 6. Wypisz dostępne wersje
|
||||
$valid_to_version = $license['valid_to_version'];
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
if ( !isset( $allowed[$ver] ) )
|
||||
continue;
|
||||
|
||||
if ( $valid_to_version && $ver > $valid_to_version )
|
||||
continue;
|
||||
|
||||
echo $ver . PHP_EOL;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l updates/versions.php
|
||||
```
|
||||
|
||||
**Step 3: Test ręczny**
|
||||
|
||||
Otwórz w przeglądarce (podaj klucz jednej z migrowanych licencji):
|
||||
```
|
||||
https://cmspro.project-dc.pl/updates/versions.php?key=TWOJ_KLUCZ
|
||||
```
|
||||
|
||||
Oczekiwane:
|
||||
- Dla klucza z `beta=0`: zwraca TYLKO wersje `channel='stable'` (pusta lista jeśli żadna jeszcze niepromowana)
|
||||
- Dla klucza z `beta=1`: zwraca wersje `channel='beta'` i `'stable'`
|
||||
- Dla nieprawidłowego klucza: brak odpowiedzi (die())
|
||||
|
||||
**Step 4: Sprawdź panel aktualizacji u klienta**
|
||||
|
||||
Na swojej testowej stronie (beta=1) otwórz `/admin/update/main_view/` — powinien widzieć dostępne wersje.
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Commit końcowy
|
||||
|
||||
**Step 1: Sprawdź status**
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff updates/versions.php
|
||||
```
|
||||
|
||||
**Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add \
|
||||
.updateignore \
|
||||
autoload/admin/factory/class.Releases.php \
|
||||
autoload/admin/controls/class.Releases.php \
|
||||
autoload/admin/view/class.Releases.php \
|
||||
admin/templates/releases/main-view.php \
|
||||
templates/additional-menu.php \
|
||||
updates/versions.php
|
||||
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: dwukanalowy system aktualizacji (beta/stable) + zarzadzanie licencjami
|
||||
|
||||
- Nowy modul admin\Releases: lista wersji z promocja beta→stable,
|
||||
CRUD licencji z flaga beta
|
||||
- versions.php czyta z DB (pp_update_licenses, pp_update_versions)
|
||||
zamiast hardkodowanej tablicy $license
|
||||
- Auto-discovery: nowe ZIPy automatycznie rejestrowane jako 'beta'
|
||||
- Calosc wykluczona z paczek klientow przez .updateignore
|
||||
|
||||
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weryfikacja end-to-end
|
||||
|
||||
Po wdrożeniu sprawdź ręcznie:
|
||||
|
||||
1. **Nowy ZIP (beta):** Wrzuć nowy ZIP na serwer → odpytaj `versions.php` kluczem beta → pojawia się nowa wersja → w panelu admina widoczna jako `beta`
|
||||
2. **Promocja:** Kliknij "Promuj →stable" → odpytaj kluczem `beta=0` → wersja pojawia się na liście
|
||||
3. **Cofnięcie:** Kliknij "Cofnij →beta" → klient `beta=0` nie widzi wersji
|
||||
4. **Licencja:** Dodaj nową licencję przez formularz → weryfikuj w phpMyAdmin
|
||||
5. **Toggle beta:** Kliknij "stable" przy licencji → zmienia się na "beta"
|
||||
6. **Zabezpieczenie:** Wgraj nową wersję CMS do klienta → sprawdź czy `admin/templates/releases/` i `autoload/admin/*/class.Releases.php` NIE znalazły się w ZIPie (build dry-run)
|
||||
14
phpunit.xml
Normal file
14
phpunit.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>autoload/Domain</directory>
|
||||
<directory>autoload/Shared</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
123
tests/Unit/Domain/Languages/LanguagesRepositoryTest.php
Normal file
123
tests/Unit/Domain/Languages/LanguagesRepositoryTest.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace Tests\Unit\Domain\Languages;
|
||||
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LanguagesRepositoryTest extends TestCase
|
||||
{
|
||||
private function mockDb(): object
|
||||
{
|
||||
return $this->createMock(\medoo::class);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
\Shared\Cache\CacheHandler::reset();
|
||||
}
|
||||
|
||||
// --- languagesList ---
|
||||
|
||||
public function testLanguagesListReturnsArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn([['id' => 'pl', 'name' => 'Polski']]);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame([['id' => 'pl', 'name' => 'Polski']], $repo->languagesList());
|
||||
}
|
||||
|
||||
public function testLanguagesListReturnsEmptyWhenNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame([], $repo->languagesList());
|
||||
}
|
||||
|
||||
// --- languageDetails ---
|
||||
|
||||
public function testLanguageDetailsReturnsRowWhenFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 'pl', 'name' => 'Polski']);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame('pl', $repo->languageDetails('pl')['id']);
|
||||
}
|
||||
|
||||
public function testLanguageDetailsReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertNull($repo->languageDetails('xx'));
|
||||
}
|
||||
|
||||
// --- activeLanguages ---
|
||||
|
||||
public function testActiveLanguagesQueriesDbAndCaches(): void
|
||||
{
|
||||
$expected = [['id' => 'pl', 'name' => 'Polski', 'domain' => null]];
|
||||
$db = $this->mockDb();
|
||||
$db->expects($this->once())->method('select')->willReturn($expected);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame($expected, $repo->activeLanguages());
|
||||
// Drugi odczyt — z cache (mock select nie zostanie wywołany drugi raz)
|
||||
$this->assertSame($expected, $repo->activeLanguages());
|
||||
}
|
||||
|
||||
public function testActiveLanguagesReturnsEmptyWhenNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame([], $repo->activeLanguages());
|
||||
}
|
||||
|
||||
// --- maxOrder ---
|
||||
|
||||
public function testMaxOrderReturnsInteger(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('max')->willReturn('5');
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame(5, $repo->maxOrder());
|
||||
}
|
||||
|
||||
// --- translationDelete ---
|
||||
|
||||
public function testTranslationDeleteReturnsTrueOnSuccess(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertTrue($repo->translationDelete(1));
|
||||
}
|
||||
|
||||
public function testTranslationDeleteReturnsFalseOnFailure(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(0);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertFalse($repo->translationDelete(1));
|
||||
}
|
||||
|
||||
// --- translationDetails ---
|
||||
|
||||
public function testTranslationDetailsReturnsRowOrNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 1, 'text' => 'hello']);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame(['id' => 1, 'text' => 'hello'], $repo->translationDetails(1));
|
||||
}
|
||||
}
|
||||
101
tests/Unit/Domain/Settings/SettingsRepositoryTest.php
Normal file
101
tests/Unit/Domain/Settings/SettingsRepositoryTest.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
namespace Tests\Unit\Domain\Settings;
|
||||
|
||||
use Domain\Settings\SettingsRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingsRepositoryTest extends TestCase
|
||||
{
|
||||
private function mockDb(): object
|
||||
{
|
||||
return $this->createMock(\medoo::class);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
\Shared\Cache\CacheHandler::reset();
|
||||
}
|
||||
|
||||
// --- allSettings ---
|
||||
|
||||
public function testAllSettingsReturnsMappedArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn([
|
||||
['param' => 'site_name', 'value' => 'Test CMS'],
|
||||
['param' => 'email', 'value' => 'admin@test.pl'],
|
||||
]);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$result = $repo->allSettings();
|
||||
|
||||
$this->assertSame('Test CMS', $result['site_name']);
|
||||
$this->assertSame('admin@test.pl', $result['email']);
|
||||
}
|
||||
|
||||
public function testAllSettingsReturnsEmptyArrayWhenDbReturnsNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertSame([], $repo->allSettings());
|
||||
}
|
||||
|
||||
public function testAllSettingsUsesCache(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->expects($this->never())->method('select');
|
||||
|
||||
\Shared\Cache\CacheHandler::store('settings_details', ['cached' => '1']);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$result = $repo->allSettings();
|
||||
|
||||
$this->assertSame('1', $result['cached']);
|
||||
}
|
||||
|
||||
// --- update ---
|
||||
|
||||
public function testUpdateCallsDbUpdateWhenParamExists(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(1);
|
||||
$db->expects($this->once())->method('update')->willReturn(true);
|
||||
$db->expects($this->never())->method('insert');
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertTrue($repo->update('site_name', 'Nowa Nazwa'));
|
||||
}
|
||||
|
||||
public function testUpdateCallsDbInsertWhenParamMissing(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(0);
|
||||
$db->expects($this->once())->method('insert')->willReturn(true);
|
||||
$db->expects($this->never())->method('update');
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$repo->update('new_param', 'value');
|
||||
}
|
||||
|
||||
// --- visitCounter ---
|
||||
|
||||
public function testVisitCounterReturnsValue(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn('1234');
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertSame('1234', $repo->visitCounter());
|
||||
}
|
||||
|
||||
public function testVisitCounterReturnsNullWhenEmpty(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertNull($repo->visitCounter());
|
||||
}
|
||||
}
|
||||
252
tests/Unit/Domain/User/UserRepositoryTest.php
Normal file
252
tests/Unit/Domain/User/UserRepositoryTest.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
namespace Tests\Unit\Domain\User;
|
||||
|
||||
use Domain\User\UserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserRepositoryTest extends TestCase
|
||||
{
|
||||
private function mockDb(): object
|
||||
{
|
||||
return $this->createMock(\medoo::class);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
\Shared\Cache\CacheHandler::reset();
|
||||
}
|
||||
|
||||
// --- find ---
|
||||
|
||||
public function testFindReturnsUserArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 1, 'login' => 'admin']);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame('admin', $repo->find(1)['login']);
|
||||
}
|
||||
|
||||
public function testFindReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertNull($repo->find(99));
|
||||
}
|
||||
|
||||
// --- findByLogin ---
|
||||
|
||||
public function testFindByLoginReturnsUser(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 1, 'login' => 'admin']);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertNotNull($repo->findByLogin('admin'));
|
||||
}
|
||||
|
||||
// --- all ---
|
||||
|
||||
public function testAllReturnsArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn([['id' => 1], ['id' => 2]]);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertCount(2, $repo->all());
|
||||
}
|
||||
|
||||
public function testAllReturnsEmptyArrayWhenNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame([], $repo->all());
|
||||
}
|
||||
|
||||
// --- hasPrivilege ---
|
||||
|
||||
public function testHasPrivilegeReturnsTrueForAdminUser(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$repo = new UserRepository($db);
|
||||
// userId === 1 zawsze ma uprawnienia, bez zapytania do DB
|
||||
$db->expects($this->never())->method('count');
|
||||
$this->assertTrue($repo->hasPrivilege('articles', 1));
|
||||
}
|
||||
|
||||
public function testHasPrivilegeReturnsTrueWhenPrivilegeExists(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->hasPrivilege('articles', 2));
|
||||
}
|
||||
|
||||
public function testHasPrivilegeReturnsFalseWhenPrivilegeMissing(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(0);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->hasPrivilege('articles', 2));
|
||||
}
|
||||
|
||||
// --- logon ---
|
||||
|
||||
public function testLogonReturnsZeroWhenUserNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
// Pierwsze get() (sprawdź czy login istnieje) → null
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame(0, $repo->logon('unknown', 'pass'));
|
||||
}
|
||||
|
||||
public function testLogonReturnsMinusOneWhenAccountBlocked(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
// Pierwsze get() → użytkownik istnieje, drugie → konto zablokowane (null)
|
||||
$db->method('get')->willReturnOnConsecutiveCalls(
|
||||
['id' => 2, 'login' => 'user'],
|
||||
null
|
||||
);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame(-1, $repo->logon('user', 'pass'));
|
||||
}
|
||||
|
||||
public function testLogonReturnsOneOnSuccess(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturnOnConsecutiveCalls(
|
||||
['id' => 2, 'login' => 'user'], // login istnieje
|
||||
['id' => 2, 'status' => 1, 'error_logged_count' => 0], // nie zablokowany
|
||||
['id' => 2] // hasło poprawne
|
||||
);
|
||||
$db->method('update')->willReturn(true);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame(1, $repo->logon('user', 'pass'));
|
||||
}
|
||||
|
||||
// --- isLoginTaken ---
|
||||
|
||||
public function testIsLoginTakenReturnsTrueWhenExists(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn('user');
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->isLoginTaken('user'));
|
||||
}
|
||||
|
||||
public function testIsLoginTakenReturnsFalseWhenFree(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->isLoginTaken('newuser'));
|
||||
}
|
||||
|
||||
// --- verifyTwofaCode ---
|
||||
|
||||
public function testVerifyTwofaCodeReturnsFalseWhenUserNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->verifyTwofaCode(1, '123456'));
|
||||
}
|
||||
|
||||
public function testVerifyTwofaCodeReturnsFalseWhenTooManyFailedAttempts(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$user = [
|
||||
'id' => 2,
|
||||
'twofa_failed_attempts' => 5,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 600),
|
||||
'twofa_code_hash' => password_hash('123456', PASSWORD_DEFAULT),
|
||||
];
|
||||
$db->method('get')->willReturn($user);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->verifyTwofaCode(2, '123456'));
|
||||
}
|
||||
|
||||
public function testVerifyTwofaCodeReturnsFalseWhenExpired(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$user = [
|
||||
'id' => 2,
|
||||
'twofa_failed_attempts' => 0,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() - 1),
|
||||
'twofa_code_hash' => password_hash('123456', PASSWORD_DEFAULT),
|
||||
];
|
||||
$db->method('get')->willReturn($user);
|
||||
$db->method('update')->willReturn(true);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->verifyTwofaCode(2, '123456'));
|
||||
}
|
||||
|
||||
public function testVerifyTwofaCodeReturnsTrueOnValidCode(): void
|
||||
{
|
||||
$code = '123456';
|
||||
$db = $this->mockDb();
|
||||
$user = [
|
||||
'id' => 2,
|
||||
'twofa_failed_attempts' => 0,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 600),
|
||||
'twofa_code_hash' => password_hash($code, PASSWORD_DEFAULT),
|
||||
];
|
||||
// find() wywołuje get() dwa razy (raz przez verifyTwofaCode, raz przez update)
|
||||
$db->method('get')->willReturn($user);
|
||||
$db->method('update')->willReturn(true);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->verifyTwofaCode(2, $code));
|
||||
}
|
||||
|
||||
// --- delete ---
|
||||
|
||||
public function testDeleteReturnsTrueOnSuccess(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->delete(2));
|
||||
}
|
||||
|
||||
// --- save — walidacja ---
|
||||
|
||||
public function testSaveReturnsErrorWhenPasswordTooShort(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$result = $repo->save(0, 'newuser', 'on', '', '123', '123', 0, []);
|
||||
|
||||
$this->assertSame('error', $result['status']);
|
||||
}
|
||||
|
||||
public function testSaveReturnsErrorWhenPasswordsMismatch(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$result = $repo->save(0, 'newuser', 'on', '', 'password1', 'password2', 0, []);
|
||||
|
||||
$this->assertSame('error', $result['status']);
|
||||
}
|
||||
}
|
||||
21
tests/bootstrap.php
Normal file
21
tests/bootstrap.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
// Medoo ORM
|
||||
require_once __DIR__ . '/../libraries/medoo/medoo.php';
|
||||
|
||||
// Stuby — muszą być załadowane PRZED autoloaderem PSR-4,
|
||||
// żeby nie zostały nadpisane przez prawdziwe klasy
|
||||
require_once __DIR__ . '/stubs/CacheHandler.php';
|
||||
require_once __DIR__ . '/stubs/S.php';
|
||||
|
||||
// PSR-4 autoloader dla Domain\
|
||||
// Shared\ jest obsłużona przez stub powyżej — pomijamy w autoloaderze
|
||||
spl_autoload_register(function (string $class): void {
|
||||
if (strncmp($class, 'Domain\\', 7) === 0) {
|
||||
$rel = substr($class, 7);
|
||||
$file = __DIR__ . '/../autoload/Domain/' . str_replace('\\', '/', $rel) . '.php';
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
});
|
||||
24
tests/stubs/CacheHandler.php
Normal file
24
tests/stubs/CacheHandler.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace Shared\Cache;
|
||||
|
||||
class CacheHandler
|
||||
{
|
||||
private static array $store = [];
|
||||
|
||||
public static function reset(): void { self::$store = []; }
|
||||
|
||||
public static function fetch(string $key): mixed
|
||||
{
|
||||
return self::$store[$key] ?? false;
|
||||
}
|
||||
|
||||
public static function store(string $key, mixed $value, int $ttl = 0): void
|
||||
{
|
||||
self::$store[$key] = $value;
|
||||
}
|
||||
|
||||
public static function delete(string $key): void
|
||||
{
|
||||
unset(self::$store[$key]);
|
||||
}
|
||||
}
|
||||
8
tests/stubs/S.php
Normal file
8
tests/stubs/S.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
class S
|
||||
{
|
||||
public static function delete_cache(): void {}
|
||||
public static function htacces(): void {}
|
||||
public static function get_domain(string $domain = ''): ?string { return $domain ?: null; }
|
||||
public static function send_email(string $to, string $subject, string $body): bool { return true; }
|
||||
}
|
||||
BIN
updates/1.60/ver_1.691.zip
Normal file
BIN
updates/1.60/ver_1.691.zip
Normal file
Binary file not shown.
4
updates/1.60/ver_1.691_files.txt
Normal file
4
updates/1.60/ver_1.691_files.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
F: ../backup_20250512_232458.zip
|
||||
F: ../backup_tmp.json
|
||||
F: ../sitemap_cmsenproject-dcpl.xml
|
||||
F: ../sitemap_cmsproproject-dcpl.xml
|
||||
49
updates/1.60/ver_1.691_manifest.json
Normal file
49
updates/1.60/ver_1.691_manifest.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"changelog": "Refaktoryzacja DDD Faza 0+1: PSR-4 autoloader, Shared (CacheHandler, Helpers, Html, ImageManipulator, Tpl), Domain (LanguagesRepository, SettingsRepository, UserRepository), testy jednostkowe Domain\\, docs/",
|
||||
"version": "1.691",
|
||||
"files": {
|
||||
"added": [
|
||||
"autoload/Domain/Languages/LanguagesRepository.php",
|
||||
"autoload/Domain/Settings/SettingsRepository.php",
|
||||
"autoload/Domain/User/UserRepository.php",
|
||||
"autoload/Shared/Cache/CacheHandler.php",
|
||||
"autoload/Shared/Helpers/Helpers.php",
|
||||
"autoload/Shared/Html/Html.php",
|
||||
"autoload/Shared/Image/ImageManipulator.php",
|
||||
"autoload/Shared/Tpl/Tpl.php"
|
||||
],
|
||||
"deleted": [
|
||||
"backup_20250512_232458.zip",
|
||||
"backup_tmp.json",
|
||||
"sitemap_cmsenproject-dcpl.xml",
|
||||
"sitemap_cmsproproject-dcpl.xml"
|
||||
],
|
||||
"modified": [
|
||||
"admin/ajax.php",
|
||||
"admin/index.php",
|
||||
"ajax.php",
|
||||
"api.php",
|
||||
"autoload/admin/class.Site.php",
|
||||
"autoload/admin/factory/class.Languages.php",
|
||||
"autoload/admin/factory/class.Settings.php",
|
||||
"autoload/admin/factory/class.Users.php",
|
||||
"autoload/class.Cache.php",
|
||||
"autoload/class.Html.php",
|
||||
"autoload/class.Image.php",
|
||||
"autoload/class.S.php",
|
||||
"autoload/class.Tpl.php",
|
||||
"autoload/front/factory/class.Languages.php",
|
||||
"autoload/front/factory/class.Settings.php",
|
||||
"cron.php",
|
||||
"index.php"
|
||||
]
|
||||
},
|
||||
"checksum_zip": "sha256:f53230f36d391828f4e368f3fc3420d8f9430ca507a1d4f57a0988823ac22192",
|
||||
"sql": [
|
||||
|
||||
],
|
||||
"date": "2026-02-27",
|
||||
"directories_deleted": [
|
||||
|
||||
]
|
||||
}
|
||||
BIN
updates/1.60/ver_1.692.zip
Normal file
BIN
updates/1.60/ver_1.692.zip
Normal file
Binary file not shown.
24
updates/1.60/ver_1.692_manifest.json
Normal file
24
updates/1.60/ver_1.692_manifest.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"changelog": "FIX - Tpl::__isset() dla poprawnej obslugi isset() na wlasciwosciach szablonu",
|
||||
"version": "1.692",
|
||||
"files": {
|
||||
"added": [
|
||||
|
||||
],
|
||||
"deleted": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"autoload/Domain/Settings/SettingsRepository.php",
|
||||
"autoload/Shared/Tpl/Tpl.php"
|
||||
]
|
||||
},
|
||||
"checksum_zip": "sha256:bbd1c61b39b5bb4a37b618efea770bed3438d7324ab389d415d24b2d87b08bd6",
|
||||
"sql": [
|
||||
|
||||
],
|
||||
"date": "2026-02-28",
|
||||
"directories_deleted": [
|
||||
|
||||
]
|
||||
}
|
||||
BIN
updates/1.60/ver_1.693.zip
Normal file
BIN
updates/1.60/ver_1.693.zip
Normal file
Binary file not shown.
28
updates/1.60/ver_1.693_manifest.json
Normal file
28
updates/1.60/ver_1.693_manifest.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"changelog": "REF - migracja admin Pages/Layouts/Articles do Domain repositories",
|
||||
"version": "1.693",
|
||||
"files": {
|
||||
"added": [
|
||||
"autoload/Domain/Articles/ArticlesRepository.php",
|
||||
"autoload/Domain/Layouts/LayoutsRepository.php",
|
||||
"autoload/Domain/Pages/PagesRepository.php",
|
||||
"composer.phar"
|
||||
],
|
||||
"deleted": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"autoload/admin/factory/class.Articles.php",
|
||||
"autoload/admin/factory/class.Layouts.php",
|
||||
"autoload/admin/factory/class.Pages.php"
|
||||
]
|
||||
},
|
||||
"checksum_zip": "sha256:3994561d9f3df8ed887f53c903b2a26ae6d17e6b10d98c7cb5cdc59132cef7b5",
|
||||
"sql": [
|
||||
|
||||
],
|
||||
"date": "2026-03-04",
|
||||
"directories_deleted": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,458 +1,71 @@
|
||||
<?
|
||||
$current_ver = 1690;
|
||||
require_once '../config.php';
|
||||
require_once '../libraries/medoo/medoo.php';
|
||||
|
||||
for ($i = 1; $i <= $current_ver; $i++)
|
||||
$mdb = new medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$current_ver = 1693; // aktualizowane automatycznie przez build-update.ps1
|
||||
|
||||
// 1. Skan filesystem — lista istniejących ZIPów
|
||||
$versions = [];
|
||||
for ( $i = 1; $i <= $current_ver; $i++ )
|
||||
{
|
||||
$dir = substr(number_format($i / 1000, 3), 0, strlen(number_format($i / 1000, 3)) - 2) . '0';
|
||||
$version_old = number_format($i / 1000, 2);
|
||||
$version_new = number_format($i / 1000, 3);
|
||||
$dir = substr( number_format( $i / 1000, 3 ), 0, strlen( number_format( $i / 1000, 3 ) ) - 2 ) . '0';
|
||||
$version_old = number_format( $i / 1000, 2 );
|
||||
$version_new = number_format( $i / 1000, 3 );
|
||||
|
||||
if (file_exists('../updates/' . $dir . '/ver_' . $version_old . '.zip'))
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_old . '.zip' ) )
|
||||
$versions[] = $version_old;
|
||||
|
||||
if (file_exists('../updates/' . $dir . '/ver_' . $version_new . '.zip'))
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_new . '.zip' ) )
|
||||
$versions[] = $version_new;
|
||||
}
|
||||
$versions = array_unique( $versions );
|
||||
|
||||
$license['']['domain'] = 'project-pro.pl';
|
||||
$license['']['valid_to_date'] = '';
|
||||
$license['']['valid_to_version'] = '1.519';
|
||||
|
||||
$license['cd9c8211255303fbacb4add2129caff9']['domain'] = 'template01';
|
||||
$license['cd9c8211255303fbacb4add2129caff9']['valid_to_date'] = '';
|
||||
$license['cd9c8211255303fbacb4add2129caff9']['valid_to_version'] = '';
|
||||
|
||||
$license['fc32d79ec43ac1a5bdf2de69b65773f2']['domain'] = 'szablony';
|
||||
$license['fc32d79ec43ac1a5bdf2de69b65773f2']['valid_to_date'] = '';
|
||||
$license['fc32d79ec43ac1a5bdf2de69b65773f2']['valid_to_version'] = '';
|
||||
|
||||
$license['E79DB4882828CCB767DFEA0AADF185BA']['domain'] = 'project-pro.pl';
|
||||
$license['E79DB4882828CCB767DFEA0AADF185BA']['valid_to_date'] = '';
|
||||
$license['E79DB4882828CCB767DFEA0AADF185BA']['valid_to_version'] = '';
|
||||
|
||||
$license['644DD48CEA53091AEE926A7EAE7B0653']['domain'] = 'tele-maco.com';
|
||||
$license['644DD48CEA53091AEE926A7EAE7B0653']['valid_to_date'] = '';
|
||||
$license['644DD48CEA53091AEE926A7EAE7B0653']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['D1137E156E2196846D8FB10B71EB6B49']['domain'] = 'kangoor.pl';
|
||||
$license['D1137E156E2196846D8FB10B71EB6B49']['valid_to_date'] = '';
|
||||
$license['D1137E156E2196846D8FB10B71EB6B49']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['093BB09C91A9E94D7C5336547748CDAF']['domain'] = 'projektowanieogrodow-kanczugowska.pl';
|
||||
$license['093BB09C91A9E94D7C5336547748CDAF']['valid_to_date'] = '';
|
||||
$license['093BB09C91A9E94D7C5336547748CDAF']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['DFA5463207E86DD8D525340A7B63FD35']['domain'] = 'globelus.pl';
|
||||
$license['DFA5463207E86DD8D525340A7B63FD35']['valid_to_date'] = '';
|
||||
$license['DFA5463207E86DD8D525340A7B63FD35']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['35C16BE1111EEB36355E1C5AE43F8373']['domain'] = 'domkidarbeskidu.pl';
|
||||
$license['35C16BE1111EEB36355E1C5AE43F8373']['valid_to_date'] = '';
|
||||
$license['35C16BE1111EEB36355E1C5AE43F8373']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['E7750D7395576D424ABA08C81504AF65']['domain'] = 'wesolek-ropczyce.pl';
|
||||
$license['E7750D7395576D424ABA08C81504AF65']['valid_to_date'] = '';
|
||||
$license['E7750D7395576D424ABA08C81504AF65']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['410ed13e1c99b03b0ef7067a96cddd2e']['domain'] = 'nexdiag.com';
|
||||
$license['410ed13e1c99b03b0ef7067a96cddd2e']['valid_to_date'] = '';
|
||||
$license['410ed13e1c99b03b0ef7067a96cddd2e']['valid_to_version'] = '';
|
||||
|
||||
$license['7448547CE8DB5FB02594D60C828282AF']['domain'] = 'sdprog.com';
|
||||
$license['7448547CE8DB5FB02594D60C828282AF']['valid_to_date'] = '';
|
||||
$license['7448547CE8DB5FB02594D60C828282AF']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['8A75B8079231AF2C164B87A9945D9DA0']['domain'] = 'olihouse.pl';
|
||||
$license['8A75B8079231AF2C164B87A9945D9DA0']['valid_to_date'] = '';
|
||||
$license['8A75B8079231AF2C164B87A9945D9DA0']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['D8C47C064A3B117512873DE29346397D']['domain'] = 'zaufany-ksiegowy.com.pl';
|
||||
$license['D8C47C064A3B117512873DE29346397D']['valid_to_date'] = '';
|
||||
$license['D8C47C064A3B117512873DE29346397D']['valid_to_version'] = '';
|
||||
|
||||
$license['B29764D197531EF8C8D39867034433DB']['domain'] = 'bziuk.pl';
|
||||
$license['B29764D197531EF8C8D39867034433DB']['valid_to_date'] = '';
|
||||
$license['B29764D197531EF8C8D39867034433DB']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['0FD74F515F6C294BAEC97226C60FF173']['domain'] = 'feb.net.pl';
|
||||
$license['0FD74F515F6C294BAEC97226C60FF173']['valid_to_date'] = '';
|
||||
$license['0FD74F515F6C294BAEC97226C60FF173']['valid_to_version'] = '';
|
||||
|
||||
$license['9EE3E126BBC6E001F0F0184BDB36ECB3']['domain'] = 'lekkopijani.pl';
|
||||
$license['9EE3E126BBC6E001F0F0184BDB36ECB3']['valid_to_date'] = '';
|
||||
$license['9EE3E126BBC6E001F0F0184BDB36ECB3']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['B7DCF327FDA377C13E0DDC267A3CAAC2']['domain'] = 'studniazyczen.pl';
|
||||
$license['B7DCF327FDA377C13E0DDC267A3CAAC2']['valid_to_date'] = '';
|
||||
$license['B7DCF327FDA377C13E0DDC267A3CAAC2']['valid_to_version'] = '1.607';
|
||||
|
||||
$license['387DD19D13F0AB85B20B4D1F16D0C0DF']['domain'] = 'kubak.com.pl';
|
||||
$license['387DD19D13F0AB85B20B4D1F16D0C0DF']['valid_to_date'] = '';
|
||||
$license['387DD19D13F0AB85B20B4D1F16D0C0DF']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['81e85da9375bf2ad6594a5300f859c71']['domain'] = 'ctpoland.com.pl';
|
||||
$license['81e85da9375bf2ad6594a5300f859c71']['valid_to_date'] = '';
|
||||
$license['81e85da9375bf2ad6594a5300f859c71']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['677A5C20CC99261A41D30C112A0C3441']['domain'] = 'bmk-ck.pl';
|
||||
$license['677A5C20CC99261A41D30C112A0C3441']['valid_to_date'] = '';
|
||||
$license['677A5C20CC99261A41D30C112A0C3441']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['7835eb439a571df1ce63c56dd464a90a']['domain'] = 'demo.pro24.com.pl';
|
||||
$license['7835eb439a571df1ce63c56dd464a90a']['valid_to_date'] = '';
|
||||
$license['7835eb439a571df1ce63c56dd464a90a']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['4e87d09851053a1d35dfb3dd0653504d']['domain'] = 'lipinskipawel.pl';
|
||||
$license['4e87d09851053a1d35dfb3dd0653504d']['valid_to_date'] = '';
|
||||
$license['4e87d09851053a1d35dfb3dd0653504d']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['0918AB95878AD6EEE2936A77278B5C4E']['domain'] = 'iweda.pl';
|
||||
$license['0918AB95878AD6EEE2936A77278B5C4E']['valid_to_date'] = '';
|
||||
$license['0918AB95878AD6EEE2936A77278B5C4E']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['7d16b320128c1eafd43637a28272ce5a']['domain'] = 'gpb-group.pl';
|
||||
$license['7d16b320128c1eafd43637a28272ce5a']['valid_to_date'] = '';
|
||||
$license['7d16b320128c1eafd43637a28272ce5a']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['cd4aee2bce3827f446de7a60874fcc78']['domain'] = 'proxima.waw.pl';
|
||||
$license['cd4aee2bce3827f446de7a60874fcc78']['valid_to_date'] = '';
|
||||
$license['cd4aee2bce3827f446de7a60874fcc78']['valid_to_version'] = '';
|
||||
|
||||
$license['ca12bdf8718169341e2a2bd60b9b6718']['domain'] = 'kopalniarakszawa.pl';
|
||||
$license['ca12bdf8718169341e2a2bd60b9b6718']['valid_to_date'] = '';
|
||||
$license['ca12bdf8718169341e2a2bd60b9b6718']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['caeeb6ff0580a30f23dbf5d74c50021f']['domain'] = 'konfiguratorgbp.pl';
|
||||
$license['caeeb6ff0580a30f23dbf5d74c50021f']['valid_to_date'] = '';
|
||||
$license['caeeb6ff0580a30f23dbf5d74c50021f']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['f775aa50ba64a63a8e4fa6d27a75f7b5']['domain'] = 'agrofurdyna.pl';
|
||||
$license['f775aa50ba64a63a8e4fa6d27a75f7b5']['valid_to_date'] = '';
|
||||
$license['f775aa50ba64a63a8e4fa6d27a75f7b5']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['118cf28359bb9a4e9892a5fe22f934b7']['domain'] = 'ankiety.pl';
|
||||
$license['118cf28359bb9a4e9892a5fe22f934b7']['valid_to_date'] = '';
|
||||
$license['118cf28359bb9a4e9892a5fe22f934b7']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['2d9a32bb73802ebf5a5ff03cbad2fee5']['domain'] = 'dendron.net.pl';
|
||||
$license['2d9a32bb73802ebf5a5ff03cbad2fee5']['valid_to_date'] = '';
|
||||
$license['2d9a32bb73802ebf5a5ff03cbad2fee5']['valid_to_version'] = '';
|
||||
|
||||
$license['e8ddc0cc8a4292fde817506bbceea25e']['domain'] = 'orange.pl';
|
||||
$license['e8ddc0cc8a4292fde817506bbceea25e']['valid_to_date'] = '';
|
||||
$license['e8ddc0cc8a4292fde817506bbceea25e']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['e7b112bcf7da625993537913205aeb90']['domain'] = 'kobcrane-montaze.pl';
|
||||
$license['e7b112bcf7da625993537913205aeb90']['valid_to_date'] = '';
|
||||
$license['e7b112bcf7da625993537913205aeb90']['valid_to_version'] = '';
|
||||
|
||||
$license['6582e3ac1215189e745cffa6f14242b7']['domain'] = 'landeo.pl';
|
||||
$license['6582e3ac1215189e745cffa6f14242b7']['valid_to_date'] = '';
|
||||
$license['6582e3ac1215189e745cffa6f14242b7']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['8df0e9b3455b5de1d7bfbfd4924cf16a']['domain'] = '4est.pl';
|
||||
$license['8df0e9b3455b5de1d7bfbfd4924cf16a']['valid_to_date'] = '';
|
||||
$license['8df0e9b3455b5de1d7bfbfd4924cf16a']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['bfd57185b0202b23d5a2cae1ddcfbcf2']['domain'] = 'pbawers.pl';
|
||||
$license['bfd57185b0202b23d5a2cae1ddcfbcf2']['valid_to_date'] = '';
|
||||
$license['bfd57185b0202b23d5a2cae1ddcfbcf2']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['e82f945eeea9647022ab04821a8ca47d']['domain'] = 'uczciwawycena.pl';
|
||||
$license['e82f945eeea9647022ab04821a8ca47d']['valid_to_date'] = '';
|
||||
$license['e82f945eeea9647022ab04821a8ca47d']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['3936b399e94b91829e1851b2fecb5977']['domain'] = 'm3rent.pl';
|
||||
$license['3936b399e94b91829e1851b2fecb5977']['valid_to_date'] = '';
|
||||
$license['3936b399e94b91829e1851b2fecb5977']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['cbc2a0890d9974e03206e836e245c311']['domain'] = 'dhtpolska.pl';
|
||||
$license['cbc2a0890d9974e03206e836e245c311']['valid_to_date'] = '';
|
||||
$license['cbc2a0890d9974e03206e836e245c311']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['aa1177a9715b5663ba4b251082679065']['domain'] = 'recosolar.pl';
|
||||
$license['aa1177a9715b5663ba4b251082679065']['valid_to_date'] = '';
|
||||
$license['aa1177a9715b5663ba4b251082679065']['valid_to_version'] = '';
|
||||
|
||||
$license['edd8ce704a8c04940d333b0b53266610']['domain'] = 'wonderloft.pl';
|
||||
$license['edd8ce704a8c04940d333b0b53266610']['valid_to_date'] = '';
|
||||
$license['edd8ce704a8c04940d333b0b53266610']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['9adf305a9439e8f9fd78f7618e652978']['domain'] = 'kancelariaskoczek.pl';
|
||||
$license['9adf305a9439e8f9fd78f7618e652978']['valid_to_date'] = '';
|
||||
$license['9adf305a9439e8f9fd78f7618e652978']['valid_to_version'] = '';
|
||||
|
||||
$license['72b050effb57632584204046b6a298de']['domain'] = 'imperialogistyka.pl';
|
||||
$license['72b050effb57632584204046b6a298de']['valid_to_date'] = '';
|
||||
$license['72b050effb57632584204046b6a298de']['valid_to_version'] = '';
|
||||
|
||||
$license['6674cbb6c4d1edc9277b3c924a9a5005']['domain'] = 'dobrodomski.pl';
|
||||
$license['6674cbb6c4d1edc9277b3c924a9a5005']['valid_to_date'] = '';
|
||||
$license['6674cbb6c4d1edc9277b3c924a9a5005']['valid_to_version'] = '';
|
||||
|
||||
$license['fcb769550dae95039c25439e42b97077']['domain'] = 'mikro-domki.pl';
|
||||
$license['fcb769550dae95039c25439e42b97077']['valid_to_date'] = '';
|
||||
$license['fcb769550dae95039c25439e42b97077']['valid_to_version'] = '';
|
||||
|
||||
$license['d15c0512b8511c5a2bad7e02d8a74110']['domain'] = 'innovationprzeworsk.net';
|
||||
$license['d15c0512b8511c5a2bad7e02d8a74110']['valid_to_date'] = '';
|
||||
$license['d15c0512b8511c5a2bad7e02d8a74110']['valid_to_version'] = '';
|
||||
|
||||
$license['9158a44e52e5b24bd84419deebee051a']['domain'] = 'mpmkotly.pl';
|
||||
$license['9158a44e52e5b24bd84419deebee051a']['valid_to_date'] = '';
|
||||
$license['9158a44e52e5b24bd84419deebee051a']['valid_to_version'] = '';
|
||||
|
||||
$license['0bbc77cf87cde41d4761d57b0c7fe923']['domain'] = 'medologic.com';
|
||||
$license['0bbc77cf87cde41d4761d57b0c7fe923']['valid_to_date'] = '';
|
||||
$license['0bbc77cf87cde41d4761d57b0c7fe923']['valid_to_version'] = '';
|
||||
|
||||
$license['e428a539d475907921cd526ae88c9f47']['domain'] = 'dzieciusiowo.pl';
|
||||
$license['e428a539d475907921cd526ae88c9f47']['valid_to_date'] = '';
|
||||
$license['e428a539d475907921cd526ae88c9f47']['valid_to_version'] = '';
|
||||
|
||||
$license['2d0479b8cdb00fdb6a58105bc55e6cfa']['domain'] = 'nataliaroch.pl';
|
||||
$license['2d0479b8cdb00fdb6a58105bc55e6cfa']['valid_to_date'] = '';
|
||||
$license['2d0479b8cdb00fdb6a58105bc55e6cfa']['valid_to_version'] = '';
|
||||
|
||||
$license['454e89c0922aa91ce79ec4c1a5ac6054']['domain'] = 'softimi.pl';
|
||||
$license['454e89c0922aa91ce79ec4c1a5ac6054']['valid_to_date'] = '';
|
||||
$license['454e89c0922aa91ce79ec4c1a5ac6054']['valid_to_version'] = '';
|
||||
|
||||
$license['8c85f7f6aa5442e5f42cef1903f25f0b']['domain'] = 'nosmoke.pl';
|
||||
$license['8c85f7f6aa5442e5f42cef1903f25f0b']['valid_to_date'] = '';
|
||||
$license['8c85f7f6aa5442e5f42cef1903f25f0b']['valid_to_version'] = '';
|
||||
|
||||
$license['e688622c70a439ab650eacd0add35032']['domain'] = 'kudlatypies.pl';
|
||||
$license['e688622c70a439ab650eacd0add35032']['valid_to_date'] = '';
|
||||
$license['e688622c70a439ab650eacd0add35032']['valid_to_version'] = '';
|
||||
|
||||
$license['99593574ef188006af7f53715431623d']['domain'] = 'magdapara.pl';
|
||||
$license['99593574ef188006af7f53715431623d']['valid_to_date'] = '';
|
||||
$license['99593574ef188006af7f53715431623d']['valid_to_version'] = '';
|
||||
|
||||
$license['07c9408bae288ce6a7bf2b4189760960']['domain'] = 'verdavivo.pl';
|
||||
$license['07c9408bae288ce6a7bf2b4189760960']['valid_to_date'] = '';
|
||||
$license['07c9408bae288ce6a7bf2b4189760960']['valid_to_version'] = '';
|
||||
|
||||
$license['1cb534040cb2649a9b15b0becfc693da']['domain'] = 'swiatpaneli.rzeszow.pl';
|
||||
$license['1cb534040cb2649a9b15b0becfc693da']['valid_to_date'] = '';
|
||||
$license['1cb534040cb2649a9b15b0becfc693da']['valid_to_version'] = '';
|
||||
|
||||
$license['2cfdb0f74dbb7748dd06a67f076a2b70']['domain'] = 'fibnet.com.pl';
|
||||
$license['2cfdb0f74dbb7748dd06a67f076a2b70']['valid_to_date'] = '';
|
||||
$license['2cfdb0f74dbb7748dd06a67f076a2b70']['valid_to_version'] = '';
|
||||
|
||||
$license['1b1a48cdfc259963b1687d1ac88195e3']['domain'] = 'safarikamper.pl';
|
||||
$license['1b1a48cdfc259963b1687d1ac88195e3']['valid_to_date'] = '';
|
||||
$license['1b1a48cdfc259963b1687d1ac88195e3']['valid_to_version'] = '';
|
||||
|
||||
$license['8e148b983ff683837783f4ea4ccf2f71']['domain'] = 'rampol.net';
|
||||
$license['8e148b983ff683837783f4ea4ccf2f71']['valid_to_date'] = '';
|
||||
$license['8e148b983ff683837783f4ea4ccf2f71']['valid_to_version'] = '';
|
||||
|
||||
$license['40976930ccc19f6c0f08c903d68d2f85']['domain'] = 'vidok.pl';
|
||||
$license['40976930ccc19f6c0f08c903d68d2f85']['valid_to_date'] = '';
|
||||
$license['40976930ccc19f6c0f08c903d68d2f85']['valid_to_version'] = '';
|
||||
|
||||
$license['7f2e9bae197a7ca68b8ffa19fe116d14']['domain'] = 'pubmed.pl';
|
||||
$license['7f2e9bae197a7ca68b8ffa19fe116d14']['valid_to_date'] = '';
|
||||
$license['7f2e9bae197a7ca68b8ffa19fe116d14']['valid_to_version'] = '';
|
||||
|
||||
$license['a31397f5c0f290533a5418b3a7cd68f7']['domain'] = 'skoczek.pro24.link';
|
||||
$license['a31397f5c0f290533a5418b3a7cd68f7']['valid_to_date'] = '';
|
||||
$license['a31397f5c0f290533a5418b3a7cd68f7']['valid_to_version'] = '';
|
||||
|
||||
$license['599191bf2871cbf4bbf663a071c33cc1']['domain'] = 'marina-pallatium.eu';
|
||||
$license['599191bf2871cbf4bbf663a071c33cc1']['valid_to_date'] = '';
|
||||
$license['599191bf2871cbf4bbf663a071c33cc1']['valid_to_version'] = '';
|
||||
|
||||
$license['07117d04a12904e25dfe0b37e52176c7']['domain'] = 'zaufane.pl';
|
||||
$license['07117d04a12904e25dfe0b37e52176c7']['valid_to_date'] = '';
|
||||
$license['07117d04a12904e25dfe0b37e52176c7']['valid_to_version'] = '';
|
||||
|
||||
$license['3038ab96e469229478bd4d4266484a59']['domain'] = 'conflo.pl';
|
||||
$license['3038ab96e469229478bd4d4266484a59']['valid_to_date'] = '';
|
||||
$license['3038ab96e469229478bd4d4266484a59']['valid_to_version'] = '';
|
||||
|
||||
$license['25ce54d28231faad3a5854df9651e61f']['domain'] = 'inwestprofil.pl';
|
||||
$license['25ce54d28231faad3a5854df9651e61f']['valid_to_date'] = '';
|
||||
$license['25ce54d28231faad3a5854df9651e61f']['valid_to_version'] = '';
|
||||
|
||||
$license['79080dc0f7b03b50d74f00ba1f87825b']['domain'] = 'grzesiek.pro24.link';
|
||||
$license['79080dc0f7b03b50d74f00ba1f87825b']['valid_to_date'] = '';
|
||||
$license['79080dc0f7b03b50d74f00ba1f87825b']['valid_to_version'] = '';
|
||||
|
||||
$license['dec5859d7bd0eeccb8cf7016da184d5a']['domain'] = 'emerch.pl';
|
||||
$license['dec5859d7bd0eeccb8cf7016da184d5a']['valid_to_date'] = '';
|
||||
$license['dec5859d7bd0eeccb8cf7016da184d5a']['valid_to_version'] = '';
|
||||
|
||||
$license['7b1782067d011cc2d991b18e26f3e82b']['domain'] = 'enology.pl';
|
||||
$license['7b1782067d011cc2d991b18e26f3e82b']['valid_to_date'] = '';
|
||||
$license['7b1782067d011cc2d991b18e26f3e82b']['valid_to_version'] = '';
|
||||
|
||||
$license['8928ad9031cef96302c5c2f2e7686ed3']['domain'] = 'choinki-lancut.eu';
|
||||
$license['8928ad9031cef96302c5c2f2e7686ed3']['valid_to_date'] = '';
|
||||
$license['8928ad9031cef96302c5c2f2e7686ed3']['valid_to_version'] = '';
|
||||
|
||||
$license['64c01e72bff323a6dc5984f57074e393']['domain'] = 'choinki-lancut.eu';
|
||||
$license['64c01e72bff323a6dc5984f57074e393']['valid_to_date'] = '';
|
||||
$license['64c01e72bff323a6dc5984f57074e393']['valid_to_version'] = '';
|
||||
|
||||
$license['5f91caf697dfbb174bb4b7e80f57578e']['domain'] = 'wobistal.pl';
|
||||
$license['5f91caf697dfbb174bb4b7e80f57578e']['valid_to_date'] = '';
|
||||
$license['5f91caf697dfbb174bb4b7e80f57578e']['valid_to_version'] = '';
|
||||
|
||||
$license['4818078c90ac4e88c687c531c730f398']['domain'] = 'monika.pro24.link';
|
||||
$license['4818078c90ac4e88c687c531c730f398']['valid_to_date'] = '';
|
||||
$license['4818078c90ac4e88c687c531c730f398']['valid_to_version'] = '';
|
||||
|
||||
$license['fd51610b1f93c8ba96cd83c42c11777a']['domain'] = 'rodzic.eu';
|
||||
$license['fd51610b1f93c8ba96cd83c42c11777a']['valid_to_date'] = '';
|
||||
$license['fd51610b1f93c8ba96cd83c42c11777a']['valid_to_version'] = '';
|
||||
|
||||
$license['c957408692c36f05e1d12f8197aa54ae']['domain'] = 'emifloor.pl';
|
||||
$license['c957408692c36f05e1d12f8197aa54ae']['valid_to_date'] = '';
|
||||
$license['c957408692c36f05e1d12f8197aa54ae']['valid_to_version'] = '';
|
||||
|
||||
$license['cacae1bd64fe16d22a17c31941ba58a2']['domain'] = 'inwestor-zastepczy.rzeszow.pl';
|
||||
$license['cacae1bd64fe16d22a17c31941ba58a2']['valid_to_date'] = '';
|
||||
$license['cacae1bd64fe16d22a17c31941ba58a2']['valid_to_version'] = '';
|
||||
|
||||
$license['e3a8caa70a968f25bf097ad8623727c1']['domain'] = 'klimawent-lancut.pl';
|
||||
$license['e3a8caa70a968f25bf097ad8623727c1']['valid_to_date'] = '';
|
||||
$license['e3a8caa70a968f25bf097ad8623727c1']['valid_to_version'] = '';
|
||||
|
||||
$license['d41d8cd98f00b204e9800998ecf8427e']['domain'] = 'kurierolkuski.pl';
|
||||
$license['d41d8cd98f00b204e9800998ecf8427e']['valid_to_date'] = '';
|
||||
$license['d41d8cd98f00b204e9800998ecf8427e']['valid_to_version'] = '';
|
||||
|
||||
$license['a6ab4706022bd3000e48ce33d25d5081']['domain'] = 'pipir.pl';
|
||||
$license['a6ab4706022bd3000e48ce33d25d5081']['valid_to_date'] = '';
|
||||
$license['a6ab4706022bd3000e48ce33d25d5081']['valid_to_version'] = '';
|
||||
|
||||
$license['ecd142a4b8aab63383d762d4929341c8']['domain'] = 'hat-bud.pl';
|
||||
$license['ecd142a4b8aab63383d762d4929341c8']['valid_to_date'] = '';
|
||||
$license['ecd142a4b8aab63383d762d4929341c8']['valid_to_version'] = '';
|
||||
|
||||
$license['6badbcb418d51fc3d3b4f3121890c862']['domain'] = 'bip.rops.rzeszow.pl';
|
||||
$license['6badbcb418d51fc3d3b4f3121890c862']['valid_to_date'] = '';
|
||||
$license['6badbcb418d51fc3d3b4f3121890c862']['valid_to_version'] = '';
|
||||
|
||||
$license['8df2b842d2421be883c17b98b3c62eb8']['domain'] = 'haft.ertim.pl';
|
||||
$license['8df2b842d2421be883c17b98b3c62eb8']['valid_to_date'] = '';
|
||||
$license['8df2b842d2421be883c17b98b3c62eb8']['valid_to_version'] = '';
|
||||
|
||||
$license['c26efc849339d628d9d8cdc33158a9af']['domain'] = 'roximplast.pl';
|
||||
$license['c26efc849339d628d9d8cdc33158a9af']['valid_to_date'] = '';
|
||||
$license['c26efc849339d628d9d8cdc33158a9af']['valid_to_version'] = '';
|
||||
|
||||
$license['cf1fd02b4a287e235bc6ab5c2891f14d']['domain'] = 'tomaszpasko.pl';
|
||||
$license['cf1fd02b4a287e235bc6ab5c2891f14d']['valid_to_date'] = '';
|
||||
$license['cf1fd02b4a287e235bc6ab5c2891f14d']['valid_to_version'] = '';
|
||||
|
||||
$license['deb9fa5ac0c1ead8ba2a4224a4e3bd8f']['domain'] = 'symbiana.com.pl';
|
||||
$license['deb9fa5ac0c1ead8ba2a4224a4e3bd8f']['valid_to_date'] = '';
|
||||
$license['deb9fa5ac0c1ead8ba2a4224a4e3bd8f']['valid_to_version'] = '';
|
||||
|
||||
$license['549895ba2d09af4b938e41de51814ebb']['domain'] = 'pianfol.pl';
|
||||
$license['549895ba2d09af4b938e41de51814ebb']['valid_to_date'] = '';
|
||||
$license['549895ba2d09af4b938e41de51814ebb']['valid_to_version'] = '';
|
||||
|
||||
$license['076f0718e06bac1bcff90867eb0accf2']['domain'] = 'zsjasionka.szkola.pl';
|
||||
$license['076f0718e06bac1bcff90867eb0accf2']['valid_to_date'] = '';
|
||||
$license['076f0718e06bac1bcff90867eb0accf2']['valid_to_version'] = '';
|
||||
|
||||
$license['c377620734fa98b2418797f06326c718']['domain'] = 'de.vidok.com';
|
||||
$license['c377620734fa98b2418797f06326c718']['valid_to_date'] = '';
|
||||
$license['c377620734fa98b2418797f06326c718']['valid_to_version'] = '';
|
||||
|
||||
$license['2a5d8f9c8cb1423be0c366433252ddd1']['domain'] = 'kingstorage.pl';
|
||||
$license['2a5d8f9c8cb1423be0c366433252ddd1']['valid_to_date'] = '';
|
||||
$license['2a5d8f9c8cb1423be0c366433252ddd1']['valid_to_version'] = '';
|
||||
|
||||
$license['6d7813ea72268dd3a062e92be33adc59']['domain'] = 'dtf.centrumdruku24.pl';
|
||||
$license['6d7813ea72268dd3a062e92be33adc59']['valid_to_date'] = '';
|
||||
$license['6d7813ea72268dd3a062e92be33adc59']['valid_to_version'] = '';
|
||||
|
||||
$license['9ee5d052380f7b78a30656948e773705']['domain'] = 'dtf.centrumdruku24.pl';
|
||||
$license['9ee5d052380f7b78a30656948e773705']['valid_to_date'] = '';
|
||||
$license['9ee5d052380f7b78a30656948e773705']['valid_to_version'] = '';
|
||||
|
||||
$license['5ef6db1855e3d585fbbbb545034bbaf3']['domain'] = 'oknoland.mielec.pl';
|
||||
$license['5ef6db1855e3d585fbbbb545034bbaf3']['valid_to_date'] = '';
|
||||
$license['5ef6db1855e3d585fbbbb545034bbaf3']['valid_to_version'] = '';
|
||||
|
||||
$license['7fb7c4f62c94d24ff75f7931540ec75a']['domain'] = 'oknoland.mielec.pl';
|
||||
$license['7fb7c4f62c94d24ff75f7931540ec75a']['valid_to_date'] = '';
|
||||
$license['7fb7c4f62c94d24ff75f7931540ec75a']['valid_to_version'] = '';
|
||||
|
||||
$license['e2b0df429491afda870f30d4bdb313a0']['domain'] = 'przedszkole.pro24.com.pl';
|
||||
$license['e2b0df429491afda870f30d4bdb313a0']['valid_to_date'] = '';
|
||||
$license['e2b0df429491afda870f30d4bdb313a0']['valid_to_version'] = '';
|
||||
|
||||
$license['fb50f1b15be6a8af1178a2aab9a4dbdf']['domain'] = 'chmielowiec.eu';
|
||||
$license['fb50f1b15be6a8af1178a2aab9a4dbdf']['valid_to_date'] = '';
|
||||
$license['fb50f1b15be6a8af1178a2aab9a4dbdf']['valid_to_version'] = '';
|
||||
|
||||
$license['333f4c17de255dfec941fcdc60ea273a']['domain'] = 'kampery.brzezovka.pl';
|
||||
$license['333f4c17de255dfec941fcdc60ea273a']['valid_to_date'] = '';
|
||||
$license['333f4c17de255dfec941fcdc60ea273a']['valid_to_version'] = '';
|
||||
|
||||
$license['2f564150024940551b31a19b32ae9734']['domain'] = 'mogegarden.pl';
|
||||
$license['2f564150024940551b31a19b32ae9734']['valid_to_date'] = '';
|
||||
$license['2f564150024940551b31a19b32ae9734']['valid_to_version'] = '';
|
||||
|
||||
$license['78c454de6a7c40c55fbcd21440ef75a6']['domain'] = 'mogegarden.pl';
|
||||
$license['78c454de6a7c40c55fbcd21440ef75a6']['valid_to_date'] = '';
|
||||
$license['78c454de6a7c40c55fbcd21440ef75a6']['valid_to_version'] = '';
|
||||
|
||||
$license['2b353dfc72ad989727296019078a5939']['domain'] = 'manver.pl';
|
||||
$license['2b353dfc72ad989727296019078a5939']['valid_to_date'] = '';
|
||||
$license['2b353dfc72ad989727296019078a5939']['valid_to_version'] = '';
|
||||
|
||||
$license['7965db1a2cc68663077aa335aaf00d6e']['domain'] = 'dpsbabica.pl';
|
||||
$license['7965db1a2cc68663077aa335aaf00d6e']['valid_to_date'] = '';
|
||||
$license['7965db1a2cc68663077aa335aaf00d6e']['valid_to_version'] = '';
|
||||
|
||||
$license['1ac91c8663f2e371b9d3acbaeb45f184']['domain'] = 'eng.vidok.com';
|
||||
$license['1ac91c8663f2e371b9d3acbaeb45f184']['valid_to_date'] = '';
|
||||
$license['1ac91c8663f2e371b9d3acbaeb45f184']['valid_to_version'] = '';
|
||||
|
||||
$license['32967a10e80e258873376c9650206ff1']['domain'] = 'ertim.pl';
|
||||
$license['32967a10e80e258873376c9650206ff1']['valid_to_date'] = '';
|
||||
$license['32967a10e80e258873376c9650206ff1']['valid_to_version'] = '';
|
||||
|
||||
$license['797d5ede8228fff47151ec2555a71b29']['domain'] = 'patekaparments.pl';
|
||||
$license['797d5ede8228fff47151ec2555a71b29']['valid_to_date'] = '';
|
||||
$license['797d5ede8228fff47151ec2555a71b29']['valid_to_version'] = '';
|
||||
|
||||
$license['32764e943176bdd2563547046e2745cf']['domain'] = 'se.min-pan.krakow.pl';
|
||||
$license['32764e943176bdd2563547046e2745cf']['valid_to_date'] = '';
|
||||
$license['32764e943176bdd2563547046e2745cf']['valid_to_version'] = '';
|
||||
|
||||
$license['1802633572f03a814b86189b711d6979']['domain'] = 'kontrans.pl';
|
||||
$license['1802633572f03a814b86189b711d6979']['valid_to_date'] = '';
|
||||
$license['1802633572f03a814b86189b711d6979']['valid_to_version'] = '';
|
||||
|
||||
$update_key = $_GET['key'];
|
||||
if (!isset($license[$_GET['key']]))
|
||||
// 2. Walidacja klucza licencji
|
||||
$license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? '' ) ] );
|
||||
if ( !$license )
|
||||
die();
|
||||
|
||||
$valid_to_date = $license[$_GET['key']]['valid_to'];
|
||||
if ($valid_to && $valid_to < date('Y-m-d'))
|
||||
// 3. Sprawdź ważność daty
|
||||
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
||||
die();
|
||||
|
||||
$versions = array_unique($versions);
|
||||
|
||||
$valid_to_version = $license[$_GET['key']]['valid_to_version'];
|
||||
if ($valid_to_version)
|
||||
// 4. Auto-discovery: rejestruj nowe ZIPy jako beta
|
||||
$known = array_flip( $mdb->select( 'pp_update_versions', 'version', [] ) ?: [] );
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
foreach ($versions as $ver)
|
||||
if ($ver <= $valid_to_version)
|
||||
echo $ver . PHP_EOL;
|
||||
if ( !isset( $known[$ver] ) )
|
||||
{
|
||||
@$mdb->insert( 'pp_update_versions', [
|
||||
'version' => $ver,
|
||||
'channel' => 'beta',
|
||||
'created_at' => date( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
$known[$ver] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// 5. Filtruj wersje wg kanału (beta widzi beta+stable, reszta tylko stable)
|
||||
$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ];
|
||||
$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] );
|
||||
|
||||
// 6. Wypisz dostępne wersje
|
||||
$valid_to_version = $license['valid_to_version'];
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
foreach ($versions as $ver)
|
||||
echo $ver . PHP_EOL;
|
||||
}
|
||||
if ( !isset( $allowed[$ver] ) )
|
||||
continue;
|
||||
|
||||
if ( $valid_to_version && $ver > $valid_to_version )
|
||||
continue;
|
||||
|
||||
echo $ver . PHP_EOL;
|
||||
}
|
||||
|
||||
22
vendor/autoload.php
vendored
Normal file
22
vendor/autoload.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40::getLoader();
|
||||
119
vendor/bin/php-parse
vendored
Normal file
119
vendor/bin/php-parse
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../nikic/php-parser/bin/php-parse)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse';
|
||||
5
vendor/bin/php-parse.bat
vendored
Normal file
5
vendor/bin/php-parse.bat
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/php-parse
|
||||
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||
php "%BIN_TARGET%" %*
|
||||
122
vendor/bin/phpunit
vendored
Normal file
122
vendor/bin/phpunit
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../phpunit/phpunit/phpunit)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] = $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'] = array(realpath(__DIR__ . '/..'.'/phpunit/phpunit/phpunit'));
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = 'phpvfscomposer://'.$this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
$data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
|
||||
$data = str_replace('__FILE__', var_export($this->realpath, true), $data);
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpunit/phpunit/phpunit');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/phpunit/phpunit/phpunit';
|
||||
5
vendor/bin/phpunit.bat
vendored
Normal file
5
vendor/bin/phpunit.bat
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/phpunit
|
||||
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||
php "%BIN_TARGET%" %*
|
||||
579
vendor/composer/ClassLoader.php
vendored
Normal file
579
vendor/composer/ClassLoader.php
vendored
Normal file
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* List of PSR-0 prefixes
|
||||
*
|
||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
396
vendor/composer/InstalledVersions.php
vendored
Normal file
396
vendor/composer/InstalledVersions.php
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||
* @internal
|
||||
*/
|
||||
private static $selfDir = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $installedIsLocalDir;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
|
||||
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||
// all installed packages for example
|
||||
self::$installedIsLocalDir = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getSelfDir()
|
||||
{
|
||||
if (self::$selfDir === null) {
|
||||
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||
}
|
||||
|
||||
return self::$selfDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
$copiedLocalDir = false;
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
$selfDir = self::getSelfDir();
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
self::$installedByVendor[$vendorDir] = $required;
|
||||
$installed[] = $required;
|
||||
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||
self::$installed = $required;
|
||||
self::$installedIsLocalDir = true;
|
||||
}
|
||||
}
|
||||
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||
$copiedLocalDir = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
21
vendor/composer/LICENSE
vendored
Normal file
21
vendor/composer/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
1186
vendor/composer/autoload_classmap.php
vendored
Normal file
1186
vendor/composer/autoload_classmap.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11
vendor/composer/autoload_files.php
vendored
Normal file
11
vendor/composer/autoload_files.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
|
||||
);
|
||||
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
12
vendor/composer/autoload_psr4.php
vendored
Normal file
12
vendor/composer/autoload_psr4.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Tests\\' => array($baseDir . '/tests'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
|
||||
);
|
||||
48
vendor/composer/autoload_real.php
vendored
Normal file
48
vendor/composer/autoload_real.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitedf908e1f6b0e4fca8854163be177e40::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInitedf908e1f6b0e4fca8854163be177e40::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}, null, null);
|
||||
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||
$requireFile($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
1233
vendor/composer/autoload_static.php
vendored
Normal file
1233
vendor/composer/autoload_static.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1780
vendor/composer/installed.json
vendored
Normal file
1780
vendor/composer/installed.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
257
vendor/composer/installed.php
vendored
Normal file
257
vendor/composer/installed.php
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '8e6b29976c30c822440d5fc293930a9a38772801',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '8e6b29976c30c822440d5fc293930a9a38772801',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'myclabs/deep-copy' => array(
|
||||
'pretty_version' => '1.13.4',
|
||||
'version' => '1.13.4.0',
|
||||
'reference' => '07d290f0c47959fd5eed98c95ee5602db07e0b6a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../myclabs/deep-copy',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'nikic/php-parser' => array(
|
||||
'pretty_version' => 'v5.7.0',
|
||||
'version' => '5.7.0.0',
|
||||
'reference' => 'dca41cd15c2ac9d055ad70dbfd011130757d1f82',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nikic/php-parser',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phar-io/manifest' => array(
|
||||
'pretty_version' => '2.0.4',
|
||||
'version' => '2.0.4.0',
|
||||
'reference' => '54750ef60c58e43759730615a392c31c80e23176',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phar-io/manifest',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phar-io/version' => array(
|
||||
'pretty_version' => '3.2.1',
|
||||
'version' => '3.2.1.0',
|
||||
'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phar-io/version',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-code-coverage' => array(
|
||||
'pretty_version' => '10.1.16',
|
||||
'version' => '10.1.16.0',
|
||||
'reference' => '7e308268858ed6baedc8704a304727d20bc07c77',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-file-iterator' => array(
|
||||
'pretty_version' => '4.1.0',
|
||||
'version' => '4.1.0.0',
|
||||
'reference' => 'a95037b6d9e608ba092da1b23931e537cadc3c3c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-file-iterator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-invoker' => array(
|
||||
'pretty_version' => '4.0.0',
|
||||
'version' => '4.0.0.0',
|
||||
'reference' => 'f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-invoker',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-text-template' => array(
|
||||
'pretty_version' => '3.0.1',
|
||||
'version' => '3.0.1.0',
|
||||
'reference' => '0c7b06ff49e3d5072f057eb1fa59258bf287a748',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-text-template',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-timer' => array(
|
||||
'pretty_version' => '6.0.0',
|
||||
'version' => '6.0.0.0',
|
||||
'reference' => 'e2a2d67966e740530f4a3343fe2e030ffdc1161d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-timer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/phpunit' => array(
|
||||
'pretty_version' => '10.5.63',
|
||||
'version' => '10.5.63.0',
|
||||
'reference' => '33198268dad71e926626b618f3ec3966661e4d90',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/phpunit',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/cli-parser' => array(
|
||||
'pretty_version' => '2.0.1',
|
||||
'version' => '2.0.1.0',
|
||||
'reference' => 'c34583b87e7b7a8055bf6c450c2c77ce32a24084',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/cli-parser',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/code-unit' => array(
|
||||
'pretty_version' => '2.0.0',
|
||||
'version' => '2.0.0.0',
|
||||
'reference' => 'a81fee9eef0b7a76af11d121767abc44c104e503',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/code-unit',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/code-unit-reverse-lookup' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => '5e3a687f7d8ae33fb362c5c0743794bbb2420a1d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/comparator' => array(
|
||||
'pretty_version' => '5.0.5',
|
||||
'version' => '5.0.5.0',
|
||||
'reference' => '55dfef806eb7dfeb6e7a6935601fef866f8ca48d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/comparator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/complexity' => array(
|
||||
'pretty_version' => '3.2.0',
|
||||
'version' => '3.2.0.0',
|
||||
'reference' => '68ff824baeae169ec9f2137158ee529584553799',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/complexity',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/diff' => array(
|
||||
'pretty_version' => '5.1.1',
|
||||
'version' => '5.1.1.0',
|
||||
'reference' => 'c41e007b4b62af48218231d6c2275e4c9b975b2e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/diff',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/environment' => array(
|
||||
'pretty_version' => '6.1.0',
|
||||
'version' => '6.1.0.0',
|
||||
'reference' => '8074dbcd93529b357029f5cc5058fd3e43666984',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/environment',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/exporter' => array(
|
||||
'pretty_version' => '5.1.4',
|
||||
'version' => '5.1.4.0',
|
||||
'reference' => '0735b90f4da94969541dac1da743446e276defa6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/exporter',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/global-state' => array(
|
||||
'pretty_version' => '6.0.2',
|
||||
'version' => '6.0.2.0',
|
||||
'reference' => '987bafff24ecc4c9ac418cab1145b96dd6e9cbd9',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/global-state',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/lines-of-code' => array(
|
||||
'pretty_version' => '2.0.2',
|
||||
'version' => '2.0.2.0',
|
||||
'reference' => '856e7f6a75a84e339195d48c556f23be2ebf75d0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/lines-of-code',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/object-enumerator' => array(
|
||||
'pretty_version' => '5.0.0',
|
||||
'version' => '5.0.0.0',
|
||||
'reference' => '202d0e344a580d7f7d04b3fafce6933e59dae906',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/object-enumerator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/object-reflector' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => '24ed13d98130f0e7122df55d06c5c4942a577957',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/object-reflector',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/recursion-context' => array(
|
||||
'pretty_version' => '5.0.1',
|
||||
'version' => '5.0.1.0',
|
||||
'reference' => '47e34210757a2f37a97dcd207d032e1b01e64c7a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/recursion-context',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/type' => array(
|
||||
'pretty_version' => '4.0.0',
|
||||
'version' => '4.0.0.0',
|
||||
'reference' => '462699a16464c3944eefc02ebdd77882bd3925bf',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/type',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/version' => array(
|
||||
'pretty_version' => '4.0.1',
|
||||
'version' => '4.0.1.0',
|
||||
'reference' => 'c51fa83a5d8f43f1402e3f32a005e6262244ef17',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/version',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'theseer/tokenizer' => array(
|
||||
'pretty_version' => '1.3.1',
|
||||
'version' => '1.3.1.0',
|
||||
'reference' => 'b7489ce515e168639d17feec34b8847c326b0b3c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../theseer/tokenizer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
20
vendor/myclabs/deep-copy/LICENSE
vendored
Normal file
20
vendor/myclabs/deep-copy/LICENSE
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 My C-Sense
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
406
vendor/myclabs/deep-copy/README.md
vendored
Normal file
406
vendor/myclabs/deep-copy/README.md
vendored
Normal file
@@ -0,0 +1,406 @@
|
||||
# DeepCopy
|
||||
|
||||
DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.
|
||||
|
||||
[](https://packagist.org/packages/myclabs/deep-copy)
|
||||
[](https://github.com/myclabs/DeepCopy/actions/workflows/ci.yaml)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [How](#how)
|
||||
1. [Why](#why)
|
||||
1. [Using simply `clone`](#using-simply-clone)
|
||||
1. [Overriding `__clone()`](#overriding-__clone)
|
||||
1. [With `DeepCopy`](#with-deepcopy)
|
||||
1. [How it works](#how-it-works)
|
||||
1. [Going further](#going-further)
|
||||
1. [Matchers](#matchers)
|
||||
1. [Property name](#property-name)
|
||||
1. [Specific property](#specific-property)
|
||||
1. [Type](#type)
|
||||
1. [Filters](#filters)
|
||||
1. [`SetNullFilter`](#setnullfilter-filter)
|
||||
1. [`KeepFilter`](#keepfilter-filter)
|
||||
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter-filter)
|
||||
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter-filter)
|
||||
1. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
|
||||
1. [`ReplaceFilter`](#replacefilter-type-filter)
|
||||
1. [`ShallowCopyFilter`](#shallowcopyfilter-type-filter)
|
||||
1. [Edge cases](#edge-cases)
|
||||
1. [Contributing](#contributing)
|
||||
1. [Tests](#tests)
|
||||
|
||||
|
||||
## How?
|
||||
|
||||
Install with Composer:
|
||||
|
||||
```
|
||||
composer require myclabs/deep-copy
|
||||
```
|
||||
|
||||
Use it:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$myCopy = $copier->copy($myObject);
|
||||
```
|
||||
|
||||
|
||||
## Why?
|
||||
|
||||
- How do you create copies of your objects?
|
||||
|
||||
```php
|
||||
$myCopy = clone $myObject;
|
||||
```
|
||||
|
||||
- How do you create **deep** copies of your objects (i.e. copying also all the objects referenced in the properties)?
|
||||
|
||||
You use [`__clone()`](http://www.php.net/manual/en/language.oop5.cloning.php#object.clone) and implement the behavior
|
||||
yourself.
|
||||
|
||||
- But how do you handle **cycles** in the association graph?
|
||||
|
||||
Now you're in for a big mess :(
|
||||
|
||||

|
||||
|
||||
|
||||
### Using simply `clone`
|
||||
|
||||

|
||||
|
||||
|
||||
### Overriding `__clone()`
|
||||
|
||||

|
||||
|
||||
|
||||
### With `DeepCopy`
|
||||
|
||||

|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it
|
||||
keeps a hash map of all instances and thus preserves the object graph.
|
||||
|
||||
To use it:
|
||||
|
||||
```php
|
||||
use function DeepCopy\deep_copy;
|
||||
|
||||
$copy = deep_copy($var);
|
||||
```
|
||||
|
||||
Alternatively, you can create your own `DeepCopy` instance to configure it differently for example:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
|
||||
$copier = new DeepCopy(true);
|
||||
|
||||
$copy = $copier->copy($var);
|
||||
```
|
||||
|
||||
You may want to roll your own deep copy function:
|
||||
|
||||
```php
|
||||
namespace Acme;
|
||||
|
||||
use DeepCopy\DeepCopy;
|
||||
|
||||
function deep_copy($var)
|
||||
{
|
||||
static $copier = null;
|
||||
|
||||
if (null === $copier) {
|
||||
$copier = new DeepCopy(true);
|
||||
}
|
||||
|
||||
return $copier->copy($var);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Going further
|
||||
|
||||
You can add filters to customize the copy process.
|
||||
|
||||
The method to add a filter is `DeepCopy\DeepCopy::addFilter($filter, $matcher)`,
|
||||
with `$filter` implementing `DeepCopy\Filter\Filter`
|
||||
and `$matcher` implementing `DeepCopy\Matcher\Matcher`.
|
||||
|
||||
We provide some generic filters and matchers.
|
||||
|
||||
|
||||
### Matchers
|
||||
|
||||
- `DeepCopy\Matcher` applies on a object attribute.
|
||||
- `DeepCopy\TypeMatcher` applies on any element found in graph, including array elements.
|
||||
|
||||
|
||||
#### Property name
|
||||
|
||||
The `PropertyNameMatcher` will match a property by its name:
|
||||
|
||||
```php
|
||||
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||
|
||||
// Will apply a filter to any property of any objects named "id"
|
||||
$matcher = new PropertyNameMatcher('id');
|
||||
```
|
||||
|
||||
|
||||
#### Specific property
|
||||
|
||||
The `PropertyMatcher` will match a specific property of a specific class:
|
||||
|
||||
```php
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
// Will apply a filter to the property "id" of any objects of the class "MyClass"
|
||||
$matcher = new PropertyMatcher('MyClass', 'id');
|
||||
```
|
||||
|
||||
|
||||
#### Type
|
||||
|
||||
The `TypeMatcher` will match any element by its type (instance of a class or any value that could be parameter of
|
||||
[gettype()](http://php.net/manual/en/function.gettype.php) function):
|
||||
|
||||
```php
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
|
||||
// Will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
|
||||
$matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
|
||||
```
|
||||
|
||||
|
||||
### Filters
|
||||
|
||||
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
|
||||
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`
|
||||
|
||||
By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied).
|
||||
Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters.
|
||||
|
||||
|
||||
#### `SetNullFilter` (filter)
|
||||
|
||||
Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have
|
||||
any ID:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\SetNullFilter;
|
||||
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||
|
||||
$object = MyClass::load(123);
|
||||
echo $object->id; // 123
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
echo $copy->id; // null
|
||||
```
|
||||
|
||||
|
||||
#### `KeepFilter` (filter)
|
||||
|
||||
If you want a property to remain untouched (for example, an association to an object):
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\KeepFilter;
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
// $copy->category has not been touched
|
||||
```
|
||||
|
||||
|
||||
#### `ChainableFilter` (filter)
|
||||
|
||||
If you use cloning on proxy classes, you might want to apply two filters for:
|
||||
1. loading the data
|
||||
2. applying a transformation
|
||||
|
||||
You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e.
|
||||
the next ones may be applied).
|
||||
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\ChainableFilter;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
|
||||
use DeepCopy\Filter\SetNullFilter;
|
||||
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
|
||||
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
|
||||
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
echo $copy->id; // null
|
||||
```
|
||||
|
||||
|
||||
#### `DoctrineCollectionFilter` (filter)
|
||||
|
||||
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
|
||||
use DeepCopy\Matcher\PropertyTypeMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
```
|
||||
|
||||
|
||||
#### `DoctrineEmptyCollectionFilter` (filter)
|
||||
|
||||
If you use Doctrine and want to copy an entity who contains a `Collection` that you want to be reset, you can use the
|
||||
`DoctrineEmptyCollectionFilter`
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
// $copy->myProperty will return an empty collection
|
||||
```
|
||||
|
||||
|
||||
#### `DoctrineProxyFilter` (filter)
|
||||
|
||||
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
|
||||
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
|
||||
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
|
||||
**Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
|
||||
before other filters are applied!**
|
||||
We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the
|
||||
cloned lazy loaded entities.
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
|
||||
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
// $copy should now contain a clone of all entities, including those that were not yet fully loaded.
|
||||
```
|
||||
|
||||
|
||||
#### `ReplaceFilter` (type filter)
|
||||
|
||||
1. If you want to replace the value of a property:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\ReplaceFilter;
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$callback = function ($currentValue) {
|
||||
return $currentValue . ' (copy)'
|
||||
};
|
||||
$copier->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
// $copy->title will contain the data returned by the callback, e.g. 'The title (copy)'
|
||||
```
|
||||
|
||||
2. If you want to replace whole element:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\ReplaceFilter;
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$callback = function (MyClass $myClass) {
|
||||
return get_class($myClass);
|
||||
};
|
||||
$copier->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
|
||||
|
||||
$copy = $copier->copy([new MyClass, 'some string', new MyClass]);
|
||||
|
||||
// $copy will contain ['MyClass', 'some string', 'MyClass']
|
||||
```
|
||||
|
||||
|
||||
The `$callback` parameter of the `ReplaceFilter` constructor accepts any PHP callable.
|
||||
|
||||
|
||||
#### `ShallowCopyFilter` (type filter)
|
||||
|
||||
Stop *DeepCopy* from recursively copying element, using standard `clone` instead:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\ShallowCopyFilter;
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
use Mockery as m;
|
||||
|
||||
$this->deepCopy = new DeepCopy();
|
||||
$this->deepCopy->addTypeFilter(
|
||||
new ShallowCopyFilter,
|
||||
new TypeMatcher(m\MockInterface::class)
|
||||
);
|
||||
|
||||
$myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class));
|
||||
// All mocks will be just cloned, not deep copied
|
||||
```
|
||||
|
||||
|
||||
## Edge cases
|
||||
|
||||
The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are
|
||||
not applied. There is two ways for you to handle them:
|
||||
|
||||
- Implement your own `__clone()` method
|
||||
- Use a filter with a type matcher
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
DeepCopy is distributed under the MIT license.
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
Running the tests is simple:
|
||||
|
||||
```php
|
||||
vendor/bin/phpunit
|
||||
```
|
||||
|
||||
### Support
|
||||
|
||||
Get professional support via [the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-myclabs-deep-copy?utm_source=packagist-myclabs-deep-copy&utm_medium=referral&utm_campaign=readme).
|
||||
43
vendor/myclabs/deep-copy/composer.json
vendored
Normal file
43
vendor/myclabs/deep-copy/composer.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"description": "Create deep copies (clones) of your objects",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"clone",
|
||||
"copy",
|
||||
"duplicate",
|
||||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.6.8",
|
||||
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||
"phpspec/prophecy": "^1.10",
|
||||
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.6.8",
|
||||
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
},
|
||||
"files": [
|
||||
"src/DeepCopy/deep_copy.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"DeepCopyTest\\": "tests/DeepCopyTest/",
|
||||
"DeepCopy\\": "fixtures/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
328
vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
vendored
Normal file
328
vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy;
|
||||
|
||||
use ArrayObject;
|
||||
use DateInterval;
|
||||
use DatePeriod;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use DeepCopy\Exception\CloneException;
|
||||
use DeepCopy\Filter\ChainableFilter;
|
||||
use DeepCopy\Filter\Filter;
|
||||
use DeepCopy\Matcher\Matcher;
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
|
||||
use DeepCopy\TypeFilter\Date\DatePeriodFilter;
|
||||
use DeepCopy\TypeFilter\Spl\ArrayObjectFilter;
|
||||
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
use ReflectionObject;
|
||||
use ReflectionProperty;
|
||||
use SplDoublyLinkedList;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DeepCopy
|
||||
{
|
||||
/**
|
||||
* @var object[] List of objects copied.
|
||||
*/
|
||||
private $hashMap = [];
|
||||
|
||||
/**
|
||||
* Filters to apply.
|
||||
*
|
||||
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||
*/
|
||||
private $filters = [];
|
||||
|
||||
/**
|
||||
* Type Filters to apply.
|
||||
*
|
||||
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||
*/
|
||||
private $typeFilters = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $skipUncloneable = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useCloneMethod;
|
||||
|
||||
/**
|
||||
* @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used
|
||||
* instead of the regular deep cloning.
|
||||
*/
|
||||
public function __construct($useCloneMethod = false)
|
||||
{
|
||||
$this->useCloneMethod = $useCloneMethod;
|
||||
|
||||
$this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class));
|
||||
$this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class));
|
||||
$this->addTypeFilter(new DatePeriodFilter(), new TypeMatcher(DatePeriod::class));
|
||||
$this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, will not throw an exception when coming across an uncloneable property.
|
||||
*
|
||||
* @param $skipUncloneable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function skipUncloneable($skipUncloneable = true)
|
||||
{
|
||||
$this->skipUncloneable = $skipUncloneable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep copies the given object.
|
||||
*
|
||||
* @template TObject
|
||||
*
|
||||
* @param TObject $object
|
||||
*
|
||||
* @return TObject
|
||||
*/
|
||||
public function copy($object)
|
||||
{
|
||||
$this->hashMap = [];
|
||||
|
||||
return $this->recursiveCopy($object);
|
||||
}
|
||||
|
||||
public function addFilter(Filter $filter, Matcher $matcher)
|
||||
{
|
||||
$this->filters[] = [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
];
|
||||
}
|
||||
|
||||
public function prependFilter(Filter $filter, Matcher $matcher)
|
||||
{
|
||||
array_unshift($this->filters, [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
]);
|
||||
}
|
||||
|
||||
public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
|
||||
{
|
||||
$this->typeFilters[] = [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
];
|
||||
}
|
||||
|
||||
public function prependTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
|
||||
{
|
||||
array_unshift($this->typeFilters, [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
]);
|
||||
}
|
||||
|
||||
private function recursiveCopy($var)
|
||||
{
|
||||
// Matches Type Filter
|
||||
if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) {
|
||||
return $filter->apply($var);
|
||||
}
|
||||
|
||||
// Resource
|
||||
if (is_resource($var)) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
// Array
|
||||
if (is_array($var)) {
|
||||
return $this->copyArray($var);
|
||||
}
|
||||
|
||||
// Scalar
|
||||
if (! is_object($var)) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
// Enum
|
||||
if (PHP_VERSION_ID >= 80100 && enum_exists(get_class($var))) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
// Object
|
||||
return $this->copyObject($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
private function copyArray(array $array)
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
$array[$key] = $this->recursiveCopy($value);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies an object.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @throws CloneException
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
private function copyObject($object)
|
||||
{
|
||||
$objectHash = spl_object_hash($object);
|
||||
|
||||
if (isset($this->hashMap[$objectHash])) {
|
||||
return $this->hashMap[$objectHash];
|
||||
}
|
||||
|
||||
$reflectedObject = new ReflectionObject($object);
|
||||
$isCloneable = $reflectedObject->isCloneable();
|
||||
|
||||
if (false === $isCloneable) {
|
||||
if ($this->skipUncloneable) {
|
||||
$this->hashMap[$objectHash] = $object;
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
throw new CloneException(
|
||||
sprintf(
|
||||
'The class "%s" is not cloneable.',
|
||||
$reflectedObject->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$newObject = clone $object;
|
||||
$this->hashMap[$objectHash] = $newObject;
|
||||
|
||||
if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
|
||||
return $newObject;
|
||||
}
|
||||
|
||||
if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) {
|
||||
return $newObject;
|
||||
}
|
||||
|
||||
foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
|
||||
$this->copyObjectProperty($newObject, $property);
|
||||
}
|
||||
|
||||
return $newObject;
|
||||
}
|
||||
|
||||
private function copyObjectProperty($object, ReflectionProperty $property)
|
||||
{
|
||||
// Ignore static properties
|
||||
if ($property->isStatic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore readonly properties
|
||||
if (method_exists($property, 'isReadOnly') && $property->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the filters
|
||||
foreach ($this->filters as $item) {
|
||||
/** @var Matcher $matcher */
|
||||
$matcher = $item['matcher'];
|
||||
/** @var Filter $filter */
|
||||
$filter = $item['filter'];
|
||||
|
||||
if ($matcher->matches($object, $property->getName())) {
|
||||
$filter->apply(
|
||||
$object,
|
||||
$property->getName(),
|
||||
function ($object) {
|
||||
return $this->recursiveCopy($object);
|
||||
}
|
||||
);
|
||||
|
||||
if ($filter instanceof ChainableFilter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a filter matches, we stop processing this property
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
|
||||
// Ignore uninitialized properties (for PHP >7.4)
|
||||
if (method_exists($property, 'isInitialized') && !$property->isInitialized($object)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$propertyValue = $property->getValue($object);
|
||||
|
||||
// Copy the property
|
||||
$property->setValue($object, $this->recursiveCopy($propertyValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first filter that matches variable, `null` if no such filter found.
|
||||
*
|
||||
* @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and
|
||||
* 'matcher' with value of type {@see TypeMatcher}
|
||||
* @param mixed $var
|
||||
*
|
||||
* @return TypeFilter|null
|
||||
*/
|
||||
private function getFirstMatchedTypeFilter(array $filterRecords, $var)
|
||||
{
|
||||
$matched = $this->first(
|
||||
$filterRecords,
|
||||
function (array $record) use ($var) {
|
||||
/* @var TypeMatcher $matcher */
|
||||
$matcher = $record['matcher'];
|
||||
|
||||
return $matcher->matches($var);
|
||||
}
|
||||
);
|
||||
|
||||
return isset($matched) ? $matched['filter'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first element that matches predicate, `null` if no such element found.
|
||||
*
|
||||
* @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||
* @param callable $predicate Predicate arguments are: element.
|
||||
*
|
||||
* @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher'
|
||||
* with value of type {@see TypeMatcher} or `null`.
|
||||
*/
|
||||
private function first(array $elements, callable $predicate)
|
||||
{
|
||||
foreach ($elements as $element) {
|
||||
if (call_user_func($predicate, $element)) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
vendored
Normal file
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Exception;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
class CloneException extends UnexpectedValueException
|
||||
{
|
||||
}
|
||||
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
vendored
Normal file
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Exception;
|
||||
|
||||
use ReflectionException;
|
||||
|
||||
class PropertyException extends ReflectionException
|
||||
{
|
||||
}
|
||||
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
vendored
Normal file
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
/**
|
||||
* Defines a decorator filter that will not stop the chain of filters.
|
||||
*/
|
||||
class ChainableFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* @var Filter
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
public function __construct(Filter $filter)
|
||||
{
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$this->filter->apply($object, $property, $objectCopier);
|
||||
}
|
||||
}
|
||||
35
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
vendored
Normal file
35
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter\Doctrine;
|
||||
|
||||
use DeepCopy\Filter\Filter;
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineCollectionFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Copies the object property doctrine collection.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
$oldCollection = $reflectionProperty->getValue($object);
|
||||
|
||||
$newCollection = $oldCollection->map(
|
||||
function ($item) use ($objectCopier) {
|
||||
return $objectCopier($item);
|
||||
}
|
||||
);
|
||||
|
||||
$reflectionProperty->setValue($object, $newCollection);
|
||||
}
|
||||
}
|
||||
30
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
vendored
Normal file
30
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter\Doctrine;
|
||||
|
||||
use DeepCopy\Filter\Filter;
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineEmptyCollectionFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Sets the object property to an empty doctrine collection.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
* @param callable $objectCopier
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
$reflectionProperty->setValue($object, new ArrayCollection());
|
||||
}
|
||||
}
|
||||
22
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
vendored
Normal file
22
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter\Doctrine;
|
||||
|
||||
use DeepCopy\Filter\Filter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineProxyFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Triggers the magic method __load() on a Doctrine Proxy class to load the
|
||||
* actual entity from the database.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$object->__load();
|
||||
}
|
||||
}
|
||||
18
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
vendored
Normal file
18
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
/**
|
||||
* Filter to apply to a property while copying an object
|
||||
*/
|
||||
interface Filter
|
||||
{
|
||||
/**
|
||||
* Applies the filter to the object.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
* @param callable $objectCopier
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier);
|
||||
}
|
||||
16
vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
vendored
Normal file
16
vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
class KeepFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Keeps the value of the object property.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
41
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
vendored
Normal file
41
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class ReplaceFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* @param callable $callable Will be called to get the new value for each property to replace
|
||||
*/
|
||||
public function __construct(callable $callable)
|
||||
{
|
||||
$this->callback = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the object property by the result of the callback called with the object property.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
$value = call_user_func($this->callback, $reflectionProperty->getValue($object));
|
||||
|
||||
$reflectionProperty->setValue($object, $value);
|
||||
}
|
||||
}
|
||||
26
vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
vendored
Normal file
26
vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SetNullFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Sets the object property to null.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
$reflectionProperty->setValue($object, null);
|
||||
}
|
||||
}
|
||||
22
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
vendored
Normal file
22
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher\Doctrine;
|
||||
|
||||
use DeepCopy\Matcher\Matcher;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineProxyMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* Matches a Doctrine Proxy class.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
return $object instanceof Proxy;
|
||||
}
|
||||
}
|
||||
14
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
vendored
Normal file
14
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
interface Matcher
|
||||
{
|
||||
/**
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function matches($object, $property);
|
||||
}
|
||||
39
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
vendored
Normal file
39
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class PropertyMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $property;
|
||||
|
||||
/**
|
||||
* @param string $class Class name
|
||||
* @param string $property Property name
|
||||
*/
|
||||
public function __construct($class, $property)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->property = $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a specific property of a specific class.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
return ($object instanceof $this->class) && $property == $this->property;
|
||||
}
|
||||
}
|
||||
32
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
vendored
Normal file
32
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class PropertyNameMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $property;
|
||||
|
||||
/**
|
||||
* @param string $property Property name
|
||||
*/
|
||||
public function __construct($property)
|
||||
{
|
||||
$this->property = $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a property by its name.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
return $property == $this->property;
|
||||
}
|
||||
}
|
||||
54
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
vendored
Normal file
54
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
use ReflectionException;
|
||||
|
||||
/**
|
||||
* Matches a property by its type.
|
||||
*
|
||||
* It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences
|
||||
* of given type in copied context (eg. array elements), not just on object properties.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PropertyTypeMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $propertyType;
|
||||
|
||||
/**
|
||||
* @param string $propertyType Property type
|
||||
*/
|
||||
public function __construct($propertyType)
|
||||
{
|
||||
$this->propertyType = $propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
} catch (ReflectionException $exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
// Uninitialized properties (for PHP >7.4)
|
||||
if (method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) {
|
||||
// null instanceof $this->propertyType
|
||||
return false;
|
||||
}
|
||||
|
||||
return $reflectionProperty->getValue($object) instanceof $this->propertyType;
|
||||
}
|
||||
}
|
||||
78
vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
vendored
Normal file
78
vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\Reflection;
|
||||
|
||||
use DeepCopy\Exception\PropertyException;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionObject;
|
||||
use ReflectionProperty;
|
||||
|
||||
class ReflectionHelper
|
||||
{
|
||||
/**
|
||||
* Retrieves all properties (including private ones), from object and all its ancestors.
|
||||
*
|
||||
* Standard \ReflectionClass->getProperties() does not return private properties from ancestor classes.
|
||||
*
|
||||
* @author muratyaman@gmail.com
|
||||
* @see http://php.net/manual/en/reflectionclass.getproperties.php
|
||||
*
|
||||
* @param ReflectionClass $ref
|
||||
*
|
||||
* @return ReflectionProperty[]
|
||||
*/
|
||||
public static function getProperties(ReflectionClass $ref)
|
||||
{
|
||||
$props = $ref->getProperties();
|
||||
$propsArr = array();
|
||||
|
||||
foreach ($props as $prop) {
|
||||
$propertyName = $prop->getName();
|
||||
$propsArr[$propertyName] = $prop;
|
||||
}
|
||||
|
||||
if ($parentClass = $ref->getParentClass()) {
|
||||
$parentPropsArr = self::getProperties($parentClass);
|
||||
foreach ($propsArr as $key => $property) {
|
||||
$parentPropsArr[$key] = $property;
|
||||
}
|
||||
|
||||
return $parentPropsArr;
|
||||
}
|
||||
|
||||
return $propsArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves property by name from object and all its ancestors.
|
||||
*
|
||||
* @param object|string $object
|
||||
* @param string $name
|
||||
*
|
||||
* @throws PropertyException
|
||||
* @throws ReflectionException
|
||||
*
|
||||
* @return ReflectionProperty
|
||||
*/
|
||||
public static function getProperty($object, $name)
|
||||
{
|
||||
$reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object);
|
||||
|
||||
if ($reflection->hasProperty($name)) {
|
||||
return $reflection->getProperty($name);
|
||||
}
|
||||
|
||||
if ($parentClass = $reflection->getParentClass()) {
|
||||
return self::getProperty($parentClass->getName(), $name);
|
||||
}
|
||||
|
||||
throw new PropertyException(
|
||||
sprintf(
|
||||
'The class "%s" doesn\'t have a property with the given name: "%s".',
|
||||
is_object($object) ? get_class($object) : $object,
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
33
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
vendored
Normal file
33
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Date;
|
||||
|
||||
use DateInterval;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @deprecated Will be removed in 2.0. This filter will no longer be necessary in PHP 7.1+.
|
||||
*/
|
||||
class DateIntervalFilter implements TypeFilter
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param DateInterval $element
|
||||
*
|
||||
* @see http://news.php.net/php.bugs/205076
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
$copy = new DateInterval('P0D');
|
||||
|
||||
foreach ($element as $propertyName => $propertyValue) {
|
||||
$copy->{$propertyName} = $propertyValue;
|
||||
}
|
||||
|
||||
return $copy;
|
||||
}
|
||||
}
|
||||
42
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DatePeriodFilter.php
vendored
Normal file
42
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DatePeriodFilter.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Date;
|
||||
|
||||
use DatePeriod;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DatePeriodFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param DatePeriod $element
|
||||
*
|
||||
* @see http://news.php.net/php.bugs/205076
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
$options = 0;
|
||||
if (PHP_VERSION_ID >= 80200 && $element->include_end_date) {
|
||||
$options |= DatePeriod::INCLUDE_END_DATE;
|
||||
}
|
||||
if (!$element->include_start_date) {
|
||||
$options |= DatePeriod::EXCLUDE_START_DATE;
|
||||
}
|
||||
|
||||
if ($element->getEndDate()) {
|
||||
return new DatePeriod($element->getStartDate(), $element->getDateInterval(), $element->getEndDate(), $options);
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 70217) {
|
||||
$recurrences = $element->getRecurrences();
|
||||
} else {
|
||||
$recurrences = $element->recurrences - $element->include_start_date;
|
||||
}
|
||||
|
||||
return new DatePeriod($element->getStartDate(), $element->getDateInterval(), $recurrences, $options);
|
||||
}
|
||||
}
|
||||
30
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
vendored
Normal file
30
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class ReplaceFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* @param callable $callable Will be called to get the new value for each element to replace
|
||||
*/
|
||||
public function __construct(callable $callable)
|
||||
{
|
||||
$this->callback = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
return call_user_func($this->callback, $element);
|
||||
}
|
||||
}
|
||||
17
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
vendored
Normal file
17
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class ShallowCopyFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
return clone $element;
|
||||
}
|
||||
}
|
||||
36
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php
vendored
Normal file
36
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace DeepCopy\TypeFilter\Spl;
|
||||
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
|
||||
/**
|
||||
* In PHP 7.4 the storage of an ArrayObject isn't returned as
|
||||
* ReflectionProperty. So we deep copy its array copy.
|
||||
*/
|
||||
final class ArrayObjectFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* @var DeepCopy
|
||||
*/
|
||||
private $copier;
|
||||
|
||||
public function __construct(DeepCopy $copier)
|
||||
{
|
||||
$this->copier = $copier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($arrayObject)
|
||||
{
|
||||
$clone = clone $arrayObject;
|
||||
foreach ($arrayObject->getArrayCopy() as $k => $v) {
|
||||
$clone->offsetSet($k, $this->copier->copy($v));
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
|
||||
10
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php
vendored
Normal file
10
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Spl;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@see SplDoublyLinkedListFilter} instead.
|
||||
*/
|
||||
class SplDoublyLinkedList extends SplDoublyLinkedListFilter
|
||||
{
|
||||
}
|
||||
51
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php
vendored
Normal file
51
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Spl;
|
||||
|
||||
use Closure;
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
use SplDoublyLinkedList;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SplDoublyLinkedListFilter implements TypeFilter
|
||||
{
|
||||
private $copier;
|
||||
|
||||
public function __construct(DeepCopy $copier)
|
||||
{
|
||||
$this->copier = $copier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
$newElement = clone $element;
|
||||
|
||||
$copy = $this->createCopyClosure();
|
||||
|
||||
return $copy($newElement);
|
||||
}
|
||||
|
||||
private function createCopyClosure()
|
||||
{
|
||||
$copier = $this->copier;
|
||||
|
||||
$copy = function (SplDoublyLinkedList $list) use ($copier) {
|
||||
// Replace each element in the list with a deep copy of itself
|
||||
for ($i = 1; $i <= $list->count(); $i++) {
|
||||
$copy = $copier->recursiveCopy($list->shift());
|
||||
|
||||
$list->push($copy);
|
||||
}
|
||||
|
||||
return $list;
|
||||
};
|
||||
|
||||
return Closure::bind($copy, null, DeepCopy::class);
|
||||
}
|
||||
}
|
||||
13
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php
vendored
Normal file
13
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter;
|
||||
|
||||
interface TypeFilter
|
||||
{
|
||||
/**
|
||||
* Applies the filter to the object.
|
||||
*
|
||||
* @param mixed $element
|
||||
*/
|
||||
public function apply($element);
|
||||
}
|
||||
29
vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php
vendored
Normal file
29
vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy\TypeMatcher;
|
||||
|
||||
class TypeMatcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function __construct($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $element
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function matches($element)
|
||||
{
|
||||
return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type;
|
||||
}
|
||||
}
|
||||
20
vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
vendored
Normal file
20
vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace DeepCopy;
|
||||
|
||||
use function function_exists;
|
||||
|
||||
if (false === function_exists('DeepCopy\deep_copy')) {
|
||||
/**
|
||||
* Deep copies the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $useCloneMethod
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function deep_copy($value, $useCloneMethod = false)
|
||||
{
|
||||
return (new DeepCopy($useCloneMethod))->copy($value);
|
||||
}
|
||||
}
|
||||
29
vendor/nikic/php-parser/LICENSE
vendored
Normal file
29
vendor/nikic/php-parser/LICENSE
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2011, Nikita Popov
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
233
vendor/nikic/php-parser/README.md
vendored
Normal file
233
vendor/nikic/php-parser/README.md
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
PHP Parser
|
||||
==========
|
||||
|
||||
[](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
|
||||
This is a PHP parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 5.x**][doc_master] (current; for running on PHP >= 7.4; for parsing PHP 7.0 to PHP 8.4, with limited support for parsing PHP 5.x).
|
||||
|
||||
[Documentation for version 4.x][doc_4_x] (supported; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.3).
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The main features provided by this library are:
|
||||
|
||||
* Parsing PHP 7, and PHP 8 code into an abstract syntax tree (AST).
|
||||
* Invalid code can be parsed into a partial AST.
|
||||
* The AST contains accurate location information.
|
||||
* Dumping the AST in human-readable form.
|
||||
* Converting an AST back to PHP code.
|
||||
* Formatting can be preserved for partially changed ASTs.
|
||||
* Infrastructure to traverse and modify ASTs.
|
||||
* Resolution of namespaced names.
|
||||
* Evaluation of constant expressions.
|
||||
* Builders to simplify AST construction for code generation.
|
||||
* Converting an AST into JSON and back.
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
Install the library using [composer](https://getcomposer.org):
|
||||
|
||||
php composer.phar require nikic/php-parser
|
||||
|
||||
Parse some PHP code into an AST and dump the result in human-readable form:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Error;
|
||||
use PhpParser\NodeDumper;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
$code = <<<'CODE'
|
||||
<?php
|
||||
|
||||
function test($foo)
|
||||
{
|
||||
var_dump($foo);
|
||||
}
|
||||
CODE;
|
||||
|
||||
$parser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||
try {
|
||||
$ast = $parser->parse($code);
|
||||
} catch (Error $error) {
|
||||
echo "Parse error: {$error->getMessage()}\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$dumper = new NodeDumper;
|
||||
echo $dumper->dump($ast) . "\n";
|
||||
```
|
||||
|
||||
This dumps an AST looking something like this:
|
||||
|
||||
```
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_FuncCall(
|
||||
name: Name(
|
||||
name: var_dump
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
name: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Let's traverse the AST and perform some kind of modification. For example, drop all function bodies:
|
||||
|
||||
```php
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
$traverser = new NodeTraverser();
|
||||
$traverser->addVisitor(new class extends NodeVisitorAbstract {
|
||||
public function enterNode(Node $node) {
|
||||
if ($node instanceof Function_) {
|
||||
// Clean out the function body
|
||||
$node->stmts = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$ast = $traverser->traverse($ast);
|
||||
echo $dumper->dump($ast) . "\n";
|
||||
```
|
||||
|
||||
This gives us an AST where the `Function_::$stmts` are empty:
|
||||
|
||||
```
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Finally, we can convert the new AST back to PHP code:
|
||||
|
||||
```php
|
||||
use PhpParser\PrettyPrinter;
|
||||
|
||||
$prettyPrinter = new PrettyPrinter\Standard;
|
||||
echo $prettyPrinter->prettyPrintFile($ast);
|
||||
```
|
||||
|
||||
This gives us our original code, minus the `var_dump()` call inside the function:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function test($foo)
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
For a more comprehensive introduction, see the documentation.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
1. [Introduction](doc/0_Introduction.markdown)
|
||||
2. [Usage of basic components](doc/2_Usage_of_basic_components.markdown)
|
||||
|
||||
Component documentation:
|
||||
|
||||
* [Walking the AST](doc/component/Walking_the_AST.markdown)
|
||||
* Node visitors
|
||||
* Modifying the AST from a visitor
|
||||
* Short-circuiting traversals
|
||||
* Interleaved visitors
|
||||
* Simple node finding API
|
||||
* Parent and sibling references
|
||||
* [Name resolution](doc/component/Name_resolution.markdown)
|
||||
* Name resolver options
|
||||
* Name resolution context
|
||||
* [Pretty printing](doc/component/Pretty_printing.markdown)
|
||||
* Converting AST back to PHP code
|
||||
* Customizing formatting
|
||||
* Formatting-preserving code transformations
|
||||
* [AST builders](doc/component/AST_builders.markdown)
|
||||
* Fluent builders for AST nodes
|
||||
* [Lexer](doc/component/Lexer.markdown)
|
||||
* Emulation
|
||||
* Tokens, positions and attributes
|
||||
* [Error handling](doc/component/Error_handling.markdown)
|
||||
* Column information for errors
|
||||
* Error recovery (parsing of syntactically incorrect code)
|
||||
* [Constant expression evaluation](doc/component/Constant_expression_evaluation.markdown)
|
||||
* Evaluating constant/property/etc initializers
|
||||
* Handling errors and unsupported expressions
|
||||
* [JSON representation](doc/component/JSON_representation.markdown)
|
||||
* JSON encoding and decoding of ASTs
|
||||
* [Performance](doc/component/Performance.markdown)
|
||||
* Disabling Xdebug
|
||||
* Reusing objects
|
||||
* Garbage collection impact
|
||||
* [Frequently asked questions](doc/component/FAQ.markdown)
|
||||
* Parent and sibling references
|
||||
|
||||
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
||||
[doc_4_x]: https://github.com/nikic/PHP-Parser/tree/4.x/doc
|
||||
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
||||
206
vendor/nikic/php-parser/bin/php-parse
vendored
Normal file
206
vendor/nikic/php-parser/bin/php-parse
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ini_set('xdebug.max_nesting_level', 3000);
|
||||
|
||||
// Disable Xdebug var_dump() output truncation
|
||||
ini_set('xdebug.var_display_max_children', -1);
|
||||
ini_set('xdebug.var_display_max_data', -1);
|
||||
ini_set('xdebug.var_display_max_depth', -1);
|
||||
|
||||
list($operations, $files, $attributes) = parseArgs($argv);
|
||||
|
||||
/* Dump nodes by default */
|
||||
if (empty($operations)) {
|
||||
$operations[] = 'dump';
|
||||
}
|
||||
|
||||
if (empty($files)) {
|
||||
showHelp("Must specify at least one file.");
|
||||
}
|
||||
|
||||
$parser = (new PhpParser\ParserFactory())->createForVersion($attributes['version']);
|
||||
$dumper = new PhpParser\NodeDumper([
|
||||
'dumpComments' => true,
|
||||
'dumpPositions' => $attributes['with-positions'],
|
||||
]);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
|
||||
$traverser = new PhpParser\NodeTraverser();
|
||||
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file === '-') {
|
||||
$code = file_get_contents('php://stdin');
|
||||
fwrite(STDERR, "====> Stdin:\n");
|
||||
} else if (strpos($file, '<?php') === 0) {
|
||||
$code = $file;
|
||||
fwrite(STDERR, "====> Code $code\n");
|
||||
} else {
|
||||
if (!file_exists($file)) {
|
||||
fwrite(STDERR, "File $file does not exist.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$code = file_get_contents($file);
|
||||
fwrite(STDERR, "====> File $file:\n");
|
||||
}
|
||||
|
||||
if ($attributes['with-recovery']) {
|
||||
$errorHandler = new PhpParser\ErrorHandler\Collecting;
|
||||
$stmts = $parser->parse($code, $errorHandler);
|
||||
foreach ($errorHandler->getErrors() as $error) {
|
||||
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
||||
fwrite(STDERR, $message . "\n");
|
||||
}
|
||||
if (null === $stmts) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
} catch (PhpParser\Error $error) {
|
||||
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
||||
fwrite(STDERR, $message . "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
if ('dump' === $operation) {
|
||||
fwrite(STDERR, "==> Node dump:\n");
|
||||
echo $dumper->dump($stmts, $code), "\n";
|
||||
} elseif ('pretty-print' === $operation) {
|
||||
fwrite(STDERR, "==> Pretty print:\n");
|
||||
echo $prettyPrinter->prettyPrintFile($stmts), "\n";
|
||||
} elseif ('json-dump' === $operation) {
|
||||
fwrite(STDERR, "==> JSON dump:\n");
|
||||
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
|
||||
} elseif ('var-dump' === $operation) {
|
||||
fwrite(STDERR, "==> var_dump():\n");
|
||||
var_dump($stmts);
|
||||
} elseif ('resolve-names' === $operation) {
|
||||
fwrite(STDERR, "==> Resolved names.\n");
|
||||
$stmts = $traverser->traverse($stmts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) {
|
||||
if ($withColumnInfo && $e->hasColumnInfo()) {
|
||||
return $e->getMessageWithColumnInfo($code);
|
||||
} else {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function showHelp($error = '') {
|
||||
if ($error) {
|
||||
fwrite(STDERR, $error . "\n\n");
|
||||
}
|
||||
fwrite($error ? STDERR : STDOUT, <<<'OUTPUT'
|
||||
Usage: php-parse [operations] file1.php [file2.php ...]
|
||||
or: php-parse [operations] "<?php code"
|
||||
Turn PHP source code into an abstract syntax tree.
|
||||
|
||||
Operations is a list of the following options (--dump by default):
|
||||
|
||||
-d, --dump Dump nodes using NodeDumper
|
||||
-p, --pretty-print Pretty print file using PrettyPrinter\Standard
|
||||
-j, --json-dump Print json_encode() result
|
||||
--var-dump var_dump() nodes (for exact structure)
|
||||
-N, --resolve-names Resolve names using NodeVisitor\NameResolver
|
||||
-c, --with-column-info Show column-numbers for errors (if available)
|
||||
-P, --with-positions Show positions in node dumps
|
||||
-r, --with-recovery Use parsing with error recovery
|
||||
--version=VERSION Target specific PHP version (default: newest)
|
||||
-h, --help Display this page
|
||||
|
||||
Example:
|
||||
php-parse -d -p -N -d file.php
|
||||
|
||||
Dumps nodes, pretty prints them, then resolves names and dumps them again.
|
||||
|
||||
|
||||
OUTPUT
|
||||
);
|
||||
exit($error ? 1 : 0);
|
||||
}
|
||||
|
||||
function parseArgs($args) {
|
||||
$operations = [];
|
||||
$files = [];
|
||||
$attributes = [
|
||||
'with-column-info' => false,
|
||||
'with-positions' => false,
|
||||
'with-recovery' => false,
|
||||
'version' => PhpParser\PhpVersion::getNewestSupported(),
|
||||
];
|
||||
|
||||
array_shift($args);
|
||||
$parseOptions = true;
|
||||
foreach ($args as $arg) {
|
||||
if (!$parseOptions) {
|
||||
$files[] = $arg;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($arg) {
|
||||
case '--dump':
|
||||
case '-d':
|
||||
$operations[] = 'dump';
|
||||
break;
|
||||
case '--pretty-print':
|
||||
case '-p':
|
||||
$operations[] = 'pretty-print';
|
||||
break;
|
||||
case '--json-dump':
|
||||
case '-j':
|
||||
$operations[] = 'json-dump';
|
||||
break;
|
||||
case '--var-dump':
|
||||
$operations[] = 'var-dump';
|
||||
break;
|
||||
case '--resolve-names':
|
||||
case '-N':
|
||||
$operations[] = 'resolve-names';
|
||||
break;
|
||||
case '--with-column-info':
|
||||
case '-c':
|
||||
$attributes['with-column-info'] = true;
|
||||
break;
|
||||
case '--with-positions':
|
||||
case '-P':
|
||||
$attributes['with-positions'] = true;
|
||||
break;
|
||||
case '--with-recovery':
|
||||
case '-r':
|
||||
$attributes['with-recovery'] = true;
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
showHelp();
|
||||
break;
|
||||
case '--':
|
||||
$parseOptions = false;
|
||||
break;
|
||||
default:
|
||||
if (preg_match('/^--version=(.*)$/', $arg, $matches)) {
|
||||
$attributes['version'] = PhpParser\PhpVersion::fromString($matches[1]);
|
||||
} elseif ($arg[0] === '-' && \strlen($arg[0]) > 1) {
|
||||
showHelp("Invalid operation $arg.");
|
||||
} else {
|
||||
$files[] = $arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$operations, $files, $attributes];
|
||||
}
|
||||
43
vendor/nikic/php-parser/composer.json
vendored
Normal file
43
vendor/nikic/php-parser/composer.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"type": "library",
|
||||
"description": "A PHP parser written in PHP",
|
||||
"keywords": [
|
||||
"php",
|
||||
"parser"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nikita Popov"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-json": "*",
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"ircmaxell/php-yacc": "^0.0.7"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "lib/PhpParser"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "test/PhpParser/"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
]
|
||||
}
|
||||
12
vendor/nikic/php-parser/lib/PhpParser/Builder.php
vendored
Normal file
12
vendor/nikic/php-parser/lib/PhpParser/Builder.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
interface Builder {
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Node The built node
|
||||
*/
|
||||
public function getNode(): Node;
|
||||
}
|
||||
150
vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php
vendored
Normal file
150
vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Modifiers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Const_;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class ClassConst implements PhpParser\Builder {
|
||||
protected int $flags = 0;
|
||||
/** @var array<string, mixed> */
|
||||
protected array $attributes = [];
|
||||
/** @var list<Const_> */
|
||||
protected array $constants = [];
|
||||
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
/** @var Identifier|Node\Name|Node\ComplexType|null */
|
||||
protected ?Node $type = null;
|
||||
|
||||
/**
|
||||
* Creates a class constant builder
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
|
||||
*/
|
||||
public function __construct($name, $value) {
|
||||
$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another constant to const group
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addConst($name, $value) {
|
||||
$this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the constant.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes = [
|
||||
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the constant type.
|
||||
*
|
||||
* @param string|Node\Name|Identifier|Node\ComplexType $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type) {
|
||||
$this->type = BuilderHelpers::normalizeType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\ClassConst The built constant node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\ClassConst(
|
||||
$this->constants,
|
||||
$this->flags,
|
||||
$this->attributes,
|
||||
$this->attributeGroups,
|
||||
$this->type
|
||||
);
|
||||
}
|
||||
}
|
||||
151
vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php
vendored
Normal file
151
vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Modifiers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Class_ extends Declaration {
|
||||
protected string $name;
|
||||
protected ?Name $extends = null;
|
||||
/** @var list<Name> */
|
||||
protected array $implements = [];
|
||||
protected int $flags = 0;
|
||||
/** @var list<Stmt\TraitUse> */
|
||||
protected array $uses = [];
|
||||
/** @var list<Stmt\ClassConst> */
|
||||
protected array $constants = [];
|
||||
/** @var list<Stmt\Property> */
|
||||
protected array $properties = [];
|
||||
/** @var list<Stmt\ClassMethod> */
|
||||
protected array $methods = [];
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a class builder.
|
||||
*
|
||||
* @param string $name Name of the class
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends a class.
|
||||
*
|
||||
* @param Name|string $class Name of class to extend
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function extend($class) {
|
||||
$this->extends = BuilderHelpers::normalizeName($class);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements one or more interfaces.
|
||||
*
|
||||
* @param Name|string ...$interfaces Names of interfaces to implement
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function implement(...$interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->implements[] = BuilderHelpers::normalizeName($interface);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the class abstract.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeAbstract() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the class final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the class readonly.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeReadonly() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
if ($stmt instanceof Stmt\Property) {
|
||||
$this->properties[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||
$this->methods[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\TraitUse) {
|
||||
$this->uses[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassConst) {
|
||||
$this->constants[] = $stmt;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\Class_ The built class node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\Class_($this->name, [
|
||||
'flags' => $this->flags,
|
||||
'extends' => $this->extends,
|
||||
'implements' => $this->implements,
|
||||
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
||||
50
vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php
vendored
Normal file
50
vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
|
||||
abstract class Declaration implements PhpParser\Builder {
|
||||
/** @var array<string, mixed> */
|
||||
protected array $attributes = [];
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param PhpParser\Node\Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
abstract public function addStmt($stmt);
|
||||
|
||||
/**
|
||||
* Adds multiple statements.
|
||||
*
|
||||
* @param (PhpParser\Node\Stmt|PhpParser\Builder)[] $stmts The statements to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmts(array $stmts) {
|
||||
foreach ($stmts as $stmt) {
|
||||
$this->addStmt($stmt);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the declaration.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes['comments'] = [
|
||||
BuilderHelpers::normalizeDocComment($docComment)
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
86
vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php
vendored
Normal file
86
vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class EnumCase implements PhpParser\Builder {
|
||||
/** @var Identifier|string */
|
||||
protected $name;
|
||||
protected ?Node\Expr $value = null;
|
||||
/** @var array<string, mixed> */
|
||||
protected array $attributes = [];
|
||||
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an enum case builder.
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
*/
|
||||
public function __construct($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value.
|
||||
*
|
||||
* @param Node\Expr|string|int $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value) {
|
||||
$this->value = BuilderHelpers::normalizeValue($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the constant.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes = [
|
||||
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built enum case node.
|
||||
*
|
||||
* @return Stmt\EnumCase The built constant node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\EnumCase(
|
||||
$this->name,
|
||||
$this->value,
|
||||
$this->attributeGroups,
|
||||
$this->attributes
|
||||
);
|
||||
}
|
||||
}
|
||||
116
vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php
vendored
Normal file
116
vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Enum_ extends Declaration {
|
||||
protected string $name;
|
||||
protected ?Identifier $scalarType = null;
|
||||
/** @var list<Name> */
|
||||
protected array $implements = [];
|
||||
/** @var list<Stmt\TraitUse> */
|
||||
protected array $uses = [];
|
||||
/** @var list<Stmt\EnumCase> */
|
||||
protected array $enumCases = [];
|
||||
/** @var list<Stmt\ClassConst> */
|
||||
protected array $constants = [];
|
||||
/** @var list<Stmt\ClassMethod> */
|
||||
protected array $methods = [];
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an enum builder.
|
||||
*
|
||||
* @param string $name Name of the enum
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scalar type.
|
||||
*
|
||||
* @param string|Identifier $scalarType
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setScalarType($scalarType) {
|
||||
$this->scalarType = BuilderHelpers::normalizeType($scalarType);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements one or more interfaces.
|
||||
*
|
||||
* @param Name|string ...$interfaces Names of interfaces to implement
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function implement(...$interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->implements[] = BuilderHelpers::normalizeName($interface);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
if ($stmt instanceof Stmt\EnumCase) {
|
||||
$this->enumCases[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||
$this->methods[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\TraitUse) {
|
||||
$this->uses[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassConst) {
|
||||
$this->constants[] = $stmt;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\Enum_ The built enum node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\Enum_($this->name, [
|
||||
'scalarType' => $this->scalarType,
|
||||
'implements' => $this->implements,
|
||||
'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
||||
73
vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php
vendored
Normal file
73
vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
|
||||
abstract class FunctionLike extends Declaration {
|
||||
protected bool $returnByRef = false;
|
||||
/** @var Node\Param[] */
|
||||
protected array $params = [];
|
||||
|
||||
/** @var Node\Identifier|Node\Name|Node\ComplexType|null */
|
||||
protected ?Node $returnType = null;
|
||||
|
||||
/**
|
||||
* Make the function return by reference.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeReturnByRef() {
|
||||
$this->returnByRef = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter.
|
||||
*
|
||||
* @param Node\Param|Param $param The parameter to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addParam($param) {
|
||||
$param = BuilderHelpers::normalizeNode($param);
|
||||
|
||||
if (!$param instanceof Node\Param) {
|
||||
throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType()));
|
||||
}
|
||||
|
||||
$this->params[] = $param;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple parameters.
|
||||
*
|
||||
* @param (Node\Param|Param)[] $params The parameters to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addParams(array $params) {
|
||||
foreach ($params as $param) {
|
||||
$this->addParam($param);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the return type for PHP 7.
|
||||
*
|
||||
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setReturnType($type) {
|
||||
$this->returnType = BuilderHelpers::normalizeType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
67
vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php
vendored
Normal file
67
vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Function_ extends FunctionLike {
|
||||
protected string $name;
|
||||
/** @var list<Stmt> */
|
||||
protected array $stmts = [];
|
||||
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a function builder.
|
||||
*
|
||||
* @param string $name Name of the function
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Node|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built function node.
|
||||
*
|
||||
* @return Stmt\Function_ The built function node
|
||||
*/
|
||||
public function getNode(): Node {
|
||||
return new Stmt\Function_($this->name, [
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'returnType' => $this->returnType,
|
||||
'stmts' => $this->stmts,
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
||||
94
vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php
vendored
Normal file
94
vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Interface_ extends Declaration {
|
||||
protected string $name;
|
||||
/** @var list<Name> */
|
||||
protected array $extends = [];
|
||||
/** @var list<Stmt\ClassConst> */
|
||||
protected array $constants = [];
|
||||
/** @var list<Stmt\ClassMethod> */
|
||||
protected array $methods = [];
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an interface builder.
|
||||
*
|
||||
* @param string $name Name of the interface
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends one or more interfaces.
|
||||
*
|
||||
* @param Name|string ...$interfaces Names of interfaces to extend
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function extend(...$interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->extends[] = BuilderHelpers::normalizeName($interface);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
if ($stmt instanceof Stmt\ClassConst) {
|
||||
$this->constants[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||
// we erase all statements in the body of an interface method
|
||||
$stmt->stmts = null;
|
||||
$this->methods[] = $stmt;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built interface node.
|
||||
*
|
||||
* @return Stmt\Interface_ The built interface node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\Interface_($this->name, [
|
||||
'extends' => $this->extends,
|
||||
'stmts' => array_merge($this->constants, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user