Mój Budżet — zarządzanie finansami na własnym mikro-frameworku PHP
Aplikacja internetowa do kompleksowego zarządzania finansami osobistymi — rejestrowanie przychodów i wydatków, kategoryzowanie ich, limity miesięczne i analiza przepływów pieniężnych przez wykresy i zestawienia. Cały backend zbudowany na własnoręcznie napisanym mikro-frameworku PHP: router z regex, kontener DI przez Reflection API, silnik szablonów, walidator z 13 regułami i łańcuch 9 middleware.
01Funkcjonalności
Przychody i wydatki
Dodawanie transakcji z kategorią, metodą płatności, datą i komentarzem. Automatyczna normalizacja kwoty (przecinek → kropka).
Limity kategorii
Kategoria wydatków może mieć miesięczny limit. AJAX pobiera limit i aktualną sumę — na bieżąco pokazuje balans przed dodaniem kwoty.
Bilans z wykresami
DateRangePicker z predefiniowanymi okresami (dziś, 7 dni, miesiąc, własny). Wykresy kołowe przychodów i wydatków per kategoria. Wybór daty w localStorage.
AI doradca finansowy
Przy każdym otwarciu bilansu Gemini generuje zwięzłą analizę: Diagnoza Finansowa → Kluczowa Obserwacja → 3 Rekomendacje.
Zarządzanie kategoriami
Zakładkowy panel ustawień: dodawanie, usuwanie i edycja kategorii przychodów i wydatków (z limitem) oraz metod płatności.
Bezpieczeństwo
Sesje PHP, CSRF (token weryfikowany przy każdym POST), ochrona routów przez middleware, bcrypt, prepared statements.
02Zarządzanie transakcjami
Aplikacja umożliwia dodawanie przychodów i wydatków z pełną walidacją — kwoty, daty, kategorii i opcjonalnego komentarza. Kwoty obsługują zarówno format z kropką jak i przecinkiem jako separatorem dziesiętnym. Daty akceptowane są w formatach ze słupkami i z kropkami.
// src/App/Services/TransactionService.php — createIncome()
public function createIncome(array $formData)
{
$date = $formData['date'];
if (strpos($date, '.') !== false) {
list($day, $month, $year) = explode('.', $date);
$date = sprintf('%04d-%02d-%02d', $year, $month, $day);
}
$amount = str_replace(',', '.', trim($formData['amount']));
$this->db->query(
'INSERT INTO incomes VALUES (NULL, :user_id, :income_category_assigned_to_user_id,
:amount, :date_of_income, :income_comment)',
[
'user_id' => $_SESSION['logged_id'],
'category' => $formData['category'],
'amount' => $amount,
'date' => $date,
'comment' => $formData['comment']
]
);
$_SESSION['income_added'] = true;
}
03REST API — real-time informacja o limicie
Najbardziej interaktywny element aplikacji: przy dodawaniu wydatku każda zmiana kategorii lub wpisywanej kwoty wyzwala AJAX do endpointu, który sprawdza ustawiony limit i aktualną sumę wydatków w bieżącym miesiącu. Użytkownik widzi natychmiast, ile mu zostało i jaki będzie balans po dodaniu tej kwoty.
Zwraca limit kategorii wydatków i sumę wydaną w danym miesiącu (YEAR/MONTH z daty). Wymaga sesji — chroniony przez
AuthRequiredMiddleware.
// src/App/Services/ApiService.php
public function fetchLimit($categoryId): array|false
{
return $this->db->query(
'SELECT `limit` FROM expenses_category_assigned_to_users WHERE id = :id',
['id' => (int) $categoryId]
)->fetch();
}
public function fetchMoneyspentSoFar($categoryId, $pickedDate): array
{
return $this->db->query(
'SELECT SUM(amount) AS total_spent FROM expenses
WHERE expense_category_assigned_to_user_id = :id
AND YEAR(date_of_expense) = :year
AND MONTH(date_of_expense) = :month',
[
'id' => (int) $categoryId,
'year' => (int) substr($pickedDate, 0, 4),
'month' => (int) substr($pickedDate, 5, 2)
]
)->fetch();
}
04Własny mikro-framework PHP
Serce projektu — katalog src/Framework/ z samodzielną implementacją każdej warstwy
tradycyjnie dostarczanej przez Laravel czy Symfony. Rozumienie frameworka przez jego zbudowanie od zera.
Router — dispatch z middleware chain
// src/Framework/Router.php — middleware jako zagnieżdżone domknięcia
foreach ($allMiddleware as $middleware) {
$middlewareInstance = $container->resolve($middleware);
$action = fn() => $middlewareInstance->process($action);
}
$action(); // uruchamia cały łańcuch od zewnątrz do wewnątrz
Container — autowiring przez Reflection API
// src/Framework/Container.php
public function resolve(string $className)
{
$reflectionClass = new ReflectionClass($className);
$params = $reflectionClass->getConstructor()->getParameters();
$dependencies = [];
foreach ($params as $param) {
$type = $param->getType()->getName(); // odczytaj type hint
$dependencies[] = $this->get($type); // rekursywnie wstrzyknij
}
return $reflectionClass->newInstanceArgs($dependencies);
// → np. BalanceController(TemplateEngine, TransactionService, ApiService)
// → automatycznie, bez jednej linii konfiguracji
}
05Łańcuch middleware — 9 klas
session_start(), konfiguracja cookie (httpOnly, SameSite, secure w produkcji)bin2hex(random_bytes(32)) w sesji, udostępniany w szablonach przez addGlobal()Middleware
ValidationException, zapisuje błędy i stare dane do flash, przekierowuje z powrotemOutcomesCategoriesMiddleware
06Stack technologiczny
Backend
- PHP (strict_types)własny framework src/Framework/
- MySQL + PDOprepared statements, FETCH_ASSOC, method chaining
- Composer + PSR-4autoloading namespace App\ i Framework\
- bcrypthashowanie haseł przy rejestracji
- Gemini APIwywołanie przez file_get_contents + stream_context
- PHP Reflection APIautowiring w kontenerze DI
- CSRF (bin2hex/random_bytes)kryptograficznie bezpieczny token
Frontend
- Bootstrap 5responsywny layout, Tabs, Modal, Navbar
- jQueryDOM manipulation, AJAX, event handling
- DateRangePickerpicker z predefiniowanymi przedziałami dat
- Moment.jsformatowanie i parsowanie dat po stronie klienta
- Vanilla JS (outcome.js)real-time limit info, dynamiczne klasy CSS
- LocalStoragepersystencja wybranego okresu i aktywnego taba
- Castoro (Google Fonts)serif font dla nagłówków
07Struktura projektu
08Czego dowodzi ten projekt
Własny framework
Router, DI container, template engine, validator, 9 middleware — zbudowany od zera, dowód rozumienia architektury MVC.
Reflection API
Autowiring przez ReflectionClass — mechanizm używany przez Laravel i Symfony pod maską.
REST API + AJAX
Endpoint limit-info wywoływany dynamicznie przy każdej zmianie kategorii lub kwoty — bez przeładowania strony.
Prompt engineering
Sztywny szablon dla Gemini z twardymi ograniczeniami — strukturyzacja wyjścia LLM na potrzeby UI.
Bezpieczeństwo
CSRF z kryptograficznym tokenem, ochrona routów middleware, bcrypt, prepared statements.
Reguły OCP
13 klas implementujących RuleInterface — nowa reguła = nowa klasa, zero zmian w Validatorze.
MySQL + JOINy
Sumy przez GROUP BY, filtrowanie BETWEEN dat, INNER JOIN między transakcjami a kategoriami użytkownika.
Pełny produkt
Rejestracja → kategorie → transakcje → bilans → AI analiza — kompletny flow finansowy gotowy do użycia.
Zobacz pełny kod na GitHubie
Repozytorium zawiera pełny framework PHP, aplikację, schemat bazy danych i prompt AI doradcy finansowego.
Zobacz kod na GitHub09Aplikacja w akcji
Poniżej nagranie pokazujące aplikację w działaniu — dodawanie transakcji, real-time informacja o limicie kategorii, bilans z wykresami i DateRangePicker oraz analiza AI doradcy finansowego.