בקצה אחורי ב-PHP מקורי: שליטה מלאה וגמישות
נתחיל בתרחיש אמיתי: לקוח מגיע אלינו עם פרויקט legacy שרץ על גרסת PHP ישנה שכבר לא יכולה להתמודד עם העומס. לאחרונה ביצענו ביקורת לפרויקט כזה על PHP 5.6: זמני תגובה של 200 אלפיות השנייה, שאילתות N+1 תכופות, וסביבת אירוח משותף מתקשה. שכתבנו את המודולים המרכזיים ב-PHP 8.x מקורי — זמני התגובה ירדו ל-40 אלפיות השנייה ועלויות האחסון ירדו ב-30%. לא ניתן היה להשתמש בפריימוורק בגלל מגבלות האירוח המשותף והעלות הגבוהה של שכתוב מלא. הפתרון שלנו היה PHP מקורי עם מאפיינים מוגדרי טיפוס, attributes ומחלקות readonly. אנו מספקים backend מוכן לשימוש, תוך שמירה על שליטה מלאה ומזעור תלויות. עם ניסיון של 10+ שנים, מהנדסים מוסמכים ולמעלה מ-40 פרויקטים שהושלמו, אנו מבטיחים תוצאות. קוד מקורי יכול להיות מהיר יותר בשל היעדר overhead. צרו קשר לביקורת של הפרויקט שלכם.
למה לבחור ב-PHP מקורי ללא פריימוורק?
לפרויקטים עם עד 15–20 נקודות קצה ולוגיקה פשוטה, גישה מקורית מספקת עד 30% ביצועים טובים יותר בהשוואה ל-Laravel או Symfony — בזכות אפס overhead. אתם בוחרים בדיוק אילו ספריות לכלול: FastRoute לניתוב, php-di להזרקת תלויות, Phinx למיגרציות. שום דבר מיותר. החיסכון בתקציב יכול להגיע ל-20% מכיוון שאין עלויות רישוי פריימוורק או תלויות מנופחות. לדוגמה, API קטן טיפוסי (10 נקודות קצה) עולה בין $5,000 ל-$10,000.
אילו בעיות אנחנו פותרים?
- שאילתות N+1 ב-PDO. אנו מיישמים תבנית Repository עם איגוד נתונים באמצעות JOINs במקום שאילתות נפרדות. זה מפחית את מספר שאילתות מסד הנתונים פי 10.
- אבטחה. אנו משתמשים בהצהרות מוכנות PDO, אימות בצד השרת ו-JWT עם TTL מוגבל — הכל כתוב ידנית, ללא 'קסם' של פריימוורק. בפרויקט אחד, חשפנו פרצת XSS שפריימוורק היה מפספס.
- ביצועים. אנו ממזערים טעינת מחלקות אוטומטית, מפעילים OPCache ומבצעים פרופילינג עם Xdebug. בדיקות עומס הן חובה. במקרה אחד, התפוקה עלתה מ-500 ל-2000 RPS.
איך אנו מיישמים אימות ואבטחה?
האימות מבוסס על JWT עם TTL קצר (15 דקות). ה-refresh token מאוחסן ב-cookie מסוג http-only. אימות ופענוח טוקן ניתן לכתיבה ב-20 שורות קוד בלבד ללא כל ספרייה. לסביבת production, אנו מסתמכים על firebase/php-jwt, אך פתרון מותאם אישית עובד לצרכים בסיסיים. מדיניות CORS והגנת CSRF מוגדרות ב-middleware.
איך PDO משפר ביצועים?
PDO עם הצהרות מוכנות לא רק מגן מפני הזרקת SQL אלא גם מאפשר ביצוע חוזר של אותה שאילתה עם פרמטרים שונים. אנו משתמשים במאגר חיבורים מסוג Singleton, וחוסכים 5–10 אלפיות השנייה מכל הגדרת חיבור.
PHP מקורי מול פריימוורקים: השוואה
| פרמטר | PHP מקורי | Laravel / Symfony |
|---|---|---|
| זמן תגובה | 15–30 אלפיות השנייה | 30–60 אלפיות השנייה |
| שליטה בקוד | מלאה | מוגבלת |
| זמן פיתוח API | 2–5 שבועות | 1–3 שבועות |
| תלויות | רק הנחוצות | מסיביות |
| מתאים במיוחד ל | מיקרוסרוויסים, legacy | פרויקטים גדולים |
שלבים טיפוסיים ולוחות זמנים
| שלב | משך |
|---|---|
| ניתוח ועיצוב | 2–4 ימים |
| תשתית (ראוטר, DI, Request/Response) | 3–7 ימים |
| מודלים, PDO, Query Builder | 3–5 ימים |
| אימות (JWT) | 2–3 ימים |
| לוגיקה עסקית | 1–3 שבועות |
| בדיקות והשקה | 3–5 ימים |
איך אנחנו עושים את זה: מחסנית הטכנולוגיות והיישום
אנו משתמשים ב-PHP 8.x, PostgreSQL 15, Nginx ב-Docker. דוגמת נקודת כניסה:
<?php // public/index.php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use App\Core\Router;
use App\Core\Request;
use App\Core\Response;
use App\Middleware\CorsMiddleware;
use App\Middleware\AuthMiddleware;
$request = Request::fromGlobals();
$router = new Router();
require __DIR__ . '/../routes/api.php';
$middlewares = [
new CorsMiddleware(),
new AuthMiddleware()
];
$response = (new Pipeline($middlewares))->handle(
$request,
fn($req) => $router->dispatch($req)
);
$response->send();
הראוטר משתמש ב-FastRoute — מהיר יותר מאלטרנטיבות:
<?php
namespace App\Core;
use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use function FastRoute\simpleDispatcher;
class Router
{
private array $routes = [];
public function get(string $pattern, array $handler, array $middlewares = []): void
{
$this->routes[] = ['GET', $pattern, $handler, $middlewares];
}
public function post(string $pattern, array $handler, array $middlewares = []): void
{
$this->routes[] = ['POST', $pattern, $handler, $middlewares];
}
public function dispatch(Request $request): Response
{
$dispatcher = simpleDispatcher(function (RouteCollector $r) {
foreach ($this->routes as [$method, $pattern, $handler, $middlewares]) {
$r->addRoute($method, $pattern, [$handler, $middlewares]);
}
});
$info = $dispatcher->dispatch($request->method(), $request->path());
return match ($info[0]) {
Dispatcher::FOUND => $this->handleFound($info[1], $info[2], $request),
Dispatcher::NOT_FOUND => Response::json(['error' => 'Not Found'], 404),
Dispatcher::METHOD_NOT_ALLOWED => Response::json(['error' => 'Method Not Allowed'], 405),
};
}
private function handleFound(array $routeInfo, array $params, Request $request): Response
{
[$handler, $middlewares] = $routeInfo;
[$class, $method] = $handler;
foreach ($middlewares as $middleware) {
$result = app($middleware)->handle($request);
if ($result instanceof Response) return $result;
}
return app($class)->$method($request, $params);
}
} שכבת אימות ומסד נתונים
מאמת מותאם אישית המשתמש ב-<?php // public/index.php declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; use App\Core\Router; use App\Core\Request; use App\Core\Response; use App\Middleware\CorsMiddleware; use App\Middleware\AuthMiddleware; $request = Request::fromGlobals(); $router = new Router(); require __DIR__ . '/../routes/api.php'; $middlewares = [new CorsMiddleware(), new AuthMiddleware()]; $response = (new Pipeline($middlewares))->handle($request, fn($req) => $router->dispatch($req)); $response->send(); וב-attributes:
<?php
namespace App\Core;
class Validator
{
private array $errors = [];
public function validate(array $data, array $rules): bool
{
$this->errors = [];
foreach ($rules as $field => $fieldRules) {
foreach (explode('|', $fieldRules) as $rule) {
$this->applyRule($data, $field, $rule);
}
}
return empty($this->errors);
}
private function applyRule(array $data, string $field, string $rule): void
{
$value = $data[$field] ?? null;
[$ruleName, $param] = array_pad(explode(':', $rule, 2), 2, null);
match ($ruleName) {
'required' => !isset($data[$field]) || $data[$field] === '' ? $this->addError($field, 'required', "Поле {$field} обязательно") : null,
'min' => is_string($value) && strlen($value) < (int) $param ? $this->addError($field, 'min', "Минимум {$param} символов") : null,
'max' => is_string($value) && strlen($value) > (int) $param ? $this->addError($field, 'max', "Максимум {$param} символов") : null,
'numeric' => $value !== null && !is_numeric($value) ? $this->addError($field, 'numeric', "Поле {$field} должно быть числом") : null,
'email' => $value && !filter_var($value, FILTER_VALIDATE_EMAIL) ? $this->addError($field, 'email', 'Некорректный email') : null,
default => null
};
}
private function addError(string $field, string $rule, string $message): void
{
$this->errors[$field][$rule] = $message;
}
public function getErrors(): array
{
return $this->errors;
}
}חיבור PDO עם Singleton ו-Query Builder פשוט לפעולות נפוצות:
דוגמה ל-Query Builder מינימלי
class QueryBuilder {
private PDO $pdo;
private string $table;
private array $conditions = [];
private array $bindings = [];
private ?int $limit = null;
private ?int $offset = null;
public function __construct(string $table) {
$this->pdo = Database::getInstance();
$this->table = $table;
}
public function where(string $column, mixed $value): static {
$placeholder = ':' . $column . count($this->bindings);
$this->conditions[] = "{$column} = {$placeholder}";
$this->bindings[$placeholder] = $value;
return $this;
}
public function limit(int $limit): static {
$this->limit = $limit;
return $this;
}
public function offset(int $offset): static {
$this->offset = $offset;
return $this;
}
public function get(): array {
$sql = "SELECT * FROM {$this->table}";
if ($this->conditions) {
$sql .= ' WHERE ' . implode(' AND ', $this->conditions);
}
if ($this->limit !== null) $sql .= " LIMIT {$this->limit}";
if ($this->offset !== null) $sql .= " OFFSET {$this->offset}";
$stmt = $this->pdo->prepare($sql);
$stmt->execute($this->bindings);
return $stmt->fetchAll();
}
public function insert(array $data): int {
$columns = implode(', ', array_keys($data));
$placeholders = implode(', ', array_map(fn($k) => ":{$k}", array_keys($data)));
$stmt = $this->pdo->prepare("INSERT INTO {$this->table} ({$columns}) VALUES ({$placeholders})");
$stmt->execute($data);
return (int) $this->pdo->lastInsertId();
}
} תהליך העבודה שלנו
- ניתוח — אנו בוחנים את הארכיטקטורה הנוכחית, העומס וצווארי הבקבוק.
- עיצוב — אנו מסכמים על סכמת מסד הנתונים, הניתוב וה-middleware.
- יישום — אנו כותבים קוד ומכסים אותו בבדיקות יחידה (PHPUnit).
- בדיקות — בדיקות עומס ואינטגרציה.
- השקה — הגדרת CI/CD, ניטור ותיעוד.
מה כלול בעבודה שלנו
- קוד מקור עם הערות
- תיעוד API (OpenAPI)
- מיגרציות מסד נתונים (Phinx)
- גישה לריפוזיטורי ולשרת
- הדרכת צוות (שעתיים)
- חודש תמיכה לאחר ההשקה
לוחות זמנים ותמחור
- תשתית בסיסית (ראוטר, DI, Request/Response) — 3–7 ימים
- מודלים, PDO, Query Builder — 3–5 ימים
- אימות (JWT) — 2–3 ימים
- לוגיקה עסקית — 1–3 שבועות
סה"כ: 2–5 שבועות ל-API קטן. העלות מחושבת באופן פרטני לפי מורכבות. חיסכון בתקציב של עד 20% הוא טיפוסי בשל היעדר overhead של פריימוורק. עבור API קטן טיפוסי (10 נקודות קצה), העלות הכוללת נעה בין $5,000 ל-$10,000.
תרחישים אופטימליים ל-PHP מקורי
- API קטן — עד 15 נקודות קצה.
- אירוח משותף — ללא גישה ל-Composer או פריימוורקים.
- הגירה מ-legacy — החלפה הדרגתית של חלקי מערכת.
- ביצועים גבוהים — מיקרוסרוויס המטפל ב-1000+ RPS.
הזמינו פיתוח backend ב-PHP מקורי — קבלו ארכיטקטורה יציבה וללא פריימוורק. קבלו ייעוץ — המהנדסים שלנו יעזרו להעריך אם PHP מקורי מתאים לפרויקט שלכם. פשוט כתבו לנו.







