Teknisk Intervju: Fullstack-utveckling med PHP
Detta avsnitt innehåller vanliga tekniska intervjufrågor för fullstack-utveckling med PHP och MySQL. Frågorna täcker grundläggande PHP-syntax, objektorienterad programmering, databasinteraktion, säkerhet och CRUD-operationer.
Fråga 1: PHP Grundläggande Syntax och Variabler
Intervjuare: "Kan du förklara skillnaden mellan PHP och JavaScript när det gäller var koden exekveras? Visa också hur man deklarerar och använder variabler i PHP."
Bra svar:
<?php
// PHP körs på SERVERN (server-side)
// JavaScript körs i WEBBLÄSAREN (client-side)
// PHP-variabler börjar alltid med $
$productName = "Laptop";
$productPrice = 999.50;
// Strängkonkatenering med punkt (.)
echo "Produkten " . $productName . " kostar " . $productPrice . " kr.";
// Alternativt med interpolering i dubbla citationstecken
echo "Produkten $productName kostar $productPrice kr.";
?>
Förklaring: PHP exekveras på servern innan HTML skickas till webbläsaren, medan JavaScript körs i användarens webbläsare. PHP-variabler börjar med $
och använder .
för strängkonkatenering.
Intervjutips: Betona skillnaden mellan server-side och client-side exekvering - detta är fundamentalt för fullstack-utveckling.
Fråga 2: PHP Arrays och Loopar
Intervjuare: "Vad är skillnaden mellan indexerade och associativa arrays i PHP? Visa hur man använder foreach för båda typerna."
Bra svar:
<?php
// Indexerad array (numeriska index)
$cities = ["Stockholm", "Göteborg", "Malmö"];
// Associativ array (namngivna nycklar)
$person = [
"firstName" => "Anna",
"lastName" => "Andersson",
"age" => 30
];
// Foreach för indexerad array (endast värden)
foreach ($cities as $city) {
echo $city . "<br>";
}
// Foreach för associativ array (nyckel och värde)
foreach ($person as $key => $value) {
echo $key . ": " . $value . "<br>";
}
// Kontrollera om värde finns
if (in_array("Stockholm", $cities)) {
echo "Stockholm finns i listan";
}
// Kontrollera om nyckel finns
if (isset($person["age"])) {
echo "Ålder: " . $person["age"];
}
?>
Förklaring: Indexerade arrays använder numeriska index (0, 1, 2...), medan associativa arrays använder namngivna strängnycklar. Foreach är den mest idiomatiska loopen för arrays i PHP.
Fråga 3: PHP Funktioner och Type Hinting
Intervjuare: "Skriv en funktion som beräknar rabatt på ett pris. Använd type hinting och förklara fördelarna med strict types."
Bra svar:
<?php
declare(strict_types=1); // Aktiverar strikt typkontroll
function calculateDiscount(float $price, int $percentage): float {
if ($percentage < 0 || $percentage > 100) {
throw new InvalidArgumentException('Procentsats måste vara mellan 0-100');
}
$discountFactor = 1 - ($percentage / 100.0);
return $price * $discountFactor;
}
// Användning
try {
$originalPrice = 1000.0;
$discountedPrice = calculateDiscount($originalPrice, 20);
echo "Pris efter 20% rabatt: " . $discountedPrice . " kr";
} catch (InvalidArgumentException $e) {
echo "Fel: " . $e->getMessage();
}
?>
Förklaring: Type hinting specificerar förväntade datatyper för parametrar och returvärden. declare(strict_types=1)
förhindrar automatisk typkonvertering och fångar fel tidigare.
Fråga 4: Objektorienterad Programmering i PHP
Intervjuare: "Skapa en enkel PHP-klass för en produkt med konstruktor, privata properties och getter/setter-metoder. Förklara fördelarna med inkapsling."
Bra svar:
<?php
declare(strict_types=1);
class Product {
private string $name;
private float $price;
private int $stock;
public function __construct(string $name, float $price, int $stock = 0) {
$this->name = $name;
$this->setPrice($price); // Använd setter för validering
$this->setStock($stock);
}
// Getters
public function getName(): string {
return $this->name;
}
public function getPrice(): float {
return $this->price;
}
public function getStock(): int {
return $this->stock;
}
// Setters med validering
public function setPrice(float $price): void {
if ($price < 0) {
throw new InvalidArgumentException('Priset kan inte vara negativt');
}
$this->price = $price;
}
public function setStock(int $stock): void {
if ($stock < 0) {
throw new InvalidArgumentException('Lagersaldo kan inte vara negativt');
}
$this->stock = $stock;
}
public function isInStock(): bool {
return $this->stock > 0;
}
}
// Användning
$product = new Product("Laptop", 15999.0, 5);
echo $product->getName() . " kostar " . $product->getPrice() . " kr";
?>
Förklaring: Inkapsling (private properties + public methods) ger kontroll över hur data nås och modifieras, möjliggör validering och förhindrar direktmanipulation av objektets tillstånd.
Fråga 5: SQL och Databasinteraktion
Intervjuare: "Skriv SQL-frågor för att skapa en users-tabell och visa hur man säkert hämtar användardata med PHP PDO."
Bra svar:
-- Skapa users-tabell
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Hämta användare från specifik stad
SELECT id, username, email FROM users
WHERE city = 'Stockholm'
ORDER BY username ASC;
<?php
// PHP PDO för säker databasinteraktion
function getUserByUsername(PDO $pdo, string $username): array|false {
// Prepared statement förhindrar SQL injection
$stmt = $pdo->prepare("SELECT id, username, email, created_at
FROM users
WHERE username = :username");
$stmt->bindParam(':username', $username, PDO::PARAM_STR);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// Databasanslutning
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=myapp;charset=utf8mb4',
$username,
$password,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$user = getUserByUsername($pdo, 'kalleanka');
if ($user) {
echo "Användare: " . htmlspecialchars($user['username']);
}
} catch (PDOException $e) {
error_log("Database error: " . $e->getMessage());
echo "Databasfel uppstod";
}
?>
Förklaring: Prepared statements är avgörande för att förhindra SQL injection. PDO ger ett konsekvent interface för olika databaser.
Fråga 6: PHP Sessioner och Säkerhet
Intervjuare: "Förklara hur PHP-sessioner fungerar och visa hur man implementerar en säker inloggningsprocess."
Bra svar:
<?php
// Starta session (alltid högst upp)
session_start();
function loginUser(PDO $pdo, string $username, string $password): bool {
// Hämta användare från databas
$stmt = $pdo->prepare("SELECT id, username, password_hash
FROM users
WHERE username = :username");
$stmt->bindParam(':username', $username);
$stmt->execute();
$user = $stmt->fetch();
// Verifiera lösenord
if ($user && password_verify($password, $user['password_hash'])) {
// Regenerera session-ID för säkerhet
session_regenerate_id(true);
// Spara användarinfo i session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
return true;
}
return false;
}
function isLoggedIn(): bool {
return isset($_SESSION['user_id']);
}
function requireLogin(): void {
if (!isLoggedIn()) {
header('Location: login.php');
exit;
}
}
function logout(): void {
$_SESSION = [];
// Ta bort session cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
}
?>
Förklaring: Sessioner lagrar data på servern och använder cookies för att identifiera användare. password_verify()
och session_regenerate_id()
är viktiga säkerhetsåtgärder.
Fråga 7: CRUD Operations med PHP
Intervjuare: "Implementera grundläggande CRUD-operationer för en bloggpost med PHP och MySQL."
Bra svar:
<?php
class PostRepository {
private PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
// CREATE
public function createPost(string $title, string $content, int $userId): int {
$stmt = $this->pdo->prepare(
"INSERT INTO posts (title, content, user_id, created_at)
VALUES (:title, :content, :user_id, NOW())"
);
$stmt->bindParam(':title', $title);
$stmt->bindParam(':content', $content);
$stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
$stmt->execute();
return (int)$this->pdo->lastInsertId();
}
// READ
public function getPostById(int $id): array|false {
$stmt = $this->pdo->prepare(
"SELECT p.*, u.username
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE p.id = :id"
);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// UPDATE
public function updatePost(int $id, string $title, string $content, int $userId): bool {
$stmt = $this->pdo->prepare(
"UPDATE posts
SET title = :title, content = :content, updated_at = NOW()
WHERE id = :id AND user_id = :user_id"
);
$stmt->bindParam(':title', $title);
$stmt->bindParam(':content', $content);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
return $stmt->execute() && $stmt->rowCount() > 0;
}
// DELETE
public function deletePost(int $id, int $userId): bool {
$stmt = $this->pdo->prepare(
"DELETE FROM posts
WHERE id = :id AND user_id = :user_id"
);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
return $stmt->execute() && $stmt->rowCount() > 0;
}
}
?>
Förklaring: CRUD-operationer (Create, Read, Update, Delete) är grundläggande för datahantering. Inkludera användar-ID i UPDATE/DELETE för säkerhet.
Fråga 8: Formulärhantering och Validering
Intervjuare: "Visa hur man säkert hanterar formulärdata i PHP med validering och felhantering."
Bra svar:
<?php
function handleContactForm(): array {
$errors = [];
$data = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Hämta och sanera input
$data['name'] = trim($_POST['name'] ?? '');
$data['email'] = trim($_POST['email'] ?? '');
$data['message'] = trim($_POST['message'] ?? '');
// Validering
if (empty($data['name'])) {
$errors[] = 'Namn är obligatoriskt';
} elseif (strlen($data['name']) < 2) {
$errors[] = 'Namnet måste vara minst 2 tecken';
}
if (empty($data['email'])) {
$errors[] = 'E-post är obligatoriskt';
} elseif (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Ogiltig e-postadress';
}
if (empty($data['message'])) {
$errors[] = 'Meddelande är obligatoriskt';
} elseif (strlen($data['message']) < 10) {
$errors[] = 'Meddelandet måste vara minst 10 tecken';
}
// Om inga fel, behandla formuläret
if (empty($errors)) {
// Spara till databas, skicka e-post etc.
// Omdirigera för att förhindra dubbel inlämning
header('Location: success.php');
exit;
}
}
return ['errors' => $errors, 'data' => $data];
}
// I HTML-templaten
$result = handleContactForm();
?>
<form method="post">
<?php if (!empty($result['errors'])): ?>
<div class="errors">
<?php foreach ($result['errors'] as $error): ?>
<p><?= htmlspecialchars($error) ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<input type="text" name="name"
value="<?= htmlspecialchars($result['data']['name'] ?? '') ?>">
<input type="email" name="email"
value="<?= htmlspecialchars($result['data']['email'] ?? '') ?>">
<textarea name="message"><?= htmlspecialchars($result['data']['message'] ?? '') ?></textarea>
<button type="submit">Skicka</button>
</form>
Förklaring: Validera alltid användarinput på serversidan. Använd htmlspecialchars()
för att förhindra XSS när data skrivs ut.
Fråga 9: Filuppladdning och Säkerhet
Intervjuare: "Implementera säker filuppladdning för bilder i PHP. Vilka säkerhetsrisker finns?"
Bra svar:
<?php
function handleImageUpload(array $file): array {
$errors = [];
$uploadPath = null;
// Kontrollera att fil laddades upp utan fel
if ($file['error'] !== UPLOAD_ERR_OK) {
$errors[] = 'Filuppladdning misslyckades';
return ['errors' => $errors, 'path' => null];
}
// Validera filstorlek (max 5MB)
$maxSize = 5 * 1024 * 1024;
if ($file['size'] > $maxSize) {
$errors[] = 'Filen är för stor (max 5MB)';
}
// Validera filtyp (kontrollera MIME type OCH filändelse)
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
$allowedExts = ['jpg', 'jpeg', 'png', 'gif'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($mimeType, $allowedMimes) || !in_array($extension, $allowedExts)) {
$errors[] = 'Endast JPG, PNG och GIF-filer tillåtna';
}
if (empty($errors)) {
// Skapa säkert filnamn
$safeName = uniqid('img_', true) . '.' . $extension;
$uploadDir = __DIR__ . '/uploads/';
$targetPath = $uploadDir . $safeName;
// Skapa directory om det inte finns
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Flytta fil till slutgiltig plats
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
$uploadPath = 'uploads/' . $safeName;
} else {
$errors[] = 'Kunde inte spara filen';
}
}
return ['errors' => $errors, 'path' => $uploadPath];
}
// Användning
if (isset($_FILES['image'])) {
$result = handleImageUpload($_FILES['image']);
if (empty($result['errors'])) {
echo "Fil uppladdad: " . htmlspecialchars($result['path']);
}
}
?>
Säkerhetsrisker:
- Executable uploads: Aldrig tillåt .php, .exe filer
- Directory traversal: Använd säkra filnamn
- MIME type spoofing: Kontrollera både MIME och filändelse
- Filstorlek: Begränsa för att förhindra DoS
Fråga 10: XSS och CSRF-skydd
Intervjuare: "Förklara XSS och CSRF-attacker. Hur skyddar man sig i PHP?"
Bra svar:
XSS (Cross-Site Scripting) Skydd:
<?php
// ALLTID sanera output med htmlspecialchars()
function safeOutput(string $data): string {
return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
}
// Farlig kod (sårbar för XSS)
echo $_GET['message']; // ❌ Farligt!
// Säker kod
echo safeOutput($_GET['message']); // ✅ Säkert
// För HTML-innehåll, använd whitelist-baserad rensning
// t.ex. HTMLPurifier biblioteket
?>
CSRF (Cross-Site Request Forgery) Skydd:
<?php
session_start();
function generateCSRFToken(): string {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function validateCSRFToken(string $token): bool {
return isset($_SESSION['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $token);
}
// I formulär
$csrfToken = generateCSRFToken();
?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= $csrfToken ?>">
<!-- Övriga formulärfält -->
<button type="submit">Skicka</button>
</form>
<?php
// Vid formulärhantering
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!validateCSRFToken($_POST['csrf_token'] ?? '')) {
die('CSRF-token validation failed');
}
// Fortsätt med formulärbehandling...
}
?>
Förklaring: XSS förhindras genom att sanera all output. CSRF förhindras med unika tokens som valideras vid formulärinlämning.
Fråga 11: Error Handling och Debugging
Intervjuare: "Visa hur man implementerar robust felhantering i en PHP-applikation."
Bra svar:
<?php
// Aktivera strict types och error reporting för utveckling
declare(strict_types=1);
ini_set('display_errors', 1);
error_reporting(E_ALL);
// Custom exception klasser
class ValidationException extends Exception {}
class DatabaseException extends Exception {}
class UserService {
private PDO $pdo;
private LoggerInterface $logger;
public function createUser(array $userData): int {
try {
// Validering
$this->validateUserData($userData);
// Databasoperation
$stmt = $this->pdo->prepare(
"INSERT INTO users (username, email, password_hash)
VALUES (:username, :email, :password)"
);
$passwordHash = password_hash($userData['password'], PASSWORD_DEFAULT);
$stmt->execute([
':username' => $userData['username'],
':email' => $userData['email'],
':password' => $passwordHash
]);
$userId = (int)$this->pdo->lastInsertId();
$this->logger->info("User created", ['user_id' => $userId]);
return $userId;
} catch (ValidationException $e) {
// Logga validering fel (för utveckling)
$this->logger->warning("Validation failed", [
'error' => $e->getMessage(),
'data' => $userData
]);
throw $e; // Re-throw för hantering i controller
} catch (PDOException $e) {
// Logga databasfel (känslig info)
$this->logger->error("Database error creating user", [
'error' => $e->getMessage(),
'code' => $e->getCode()
]);
// Kasta generellt fel till användaren
throw new DatabaseException('Could not create user');
} catch (Exception $e) {
// Fånga oväntade fel
$this->logger->critical("Unexpected error", [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw new Exception('An unexpected error occurred');
}
}
private function validateUserData(array $data): void {
if (empty($data['username'])) {
throw new ValidationException('Username is required');
}
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new ValidationException('Invalid email format');
}
if (strlen($data['password']) < 8) {
throw new ValidationException('Password must be at least 8 characters');
}
}
}
// Global exception handler för produktion
set_exception_handler(function(Throwable $e) {
error_log("Uncaught exception: " . $e->getMessage());
// Visa användarvänligt meddelande
http_response_code(500);
echo "Something went wrong. Please try again later.";
});
?>
Förklaring: Använd specifika exceptions, logga tekniska fel separat från användarmeddelanden, och ha en global exception handler som backup.
Fråga 12: Performance och Optimering
Intervjuare: "Vilka tekniker kan du använda för att optimera prestanda i en PHP-applikation?"
Bra svar:
1. Databas-optimering:
<?php
// Använd index på ofta söka kolumner
// CREATE INDEX idx_username ON users(username);
// CREATE INDEX idx_email ON users(email);
// Begränsa SELECT-kolumner
$stmt = $pdo->prepare("SELECT id, username FROM users WHERE active = 1");
// Istället för: SELECT * FROM users WHERE active = 1
// Använd LIMIT för paginering
$stmt = $pdo->prepare("SELECT * FROM posts ORDER BY created_at DESC LIMIT :offset, :limit");
$stmt->bindValue(':offset', ($page - 1) * $perPage, PDO::PARAM_INT);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
?>
2. Caching:
<?php
// APCu cache för smådata
function getCachedUserCount(): int {
$key = 'user_count';
$count = apcu_fetch($key);
if ($count === false) {
// Hämta från databas
$stmt = $pdo->query("SELECT COUNT(*) FROM users");
$count = (int)$stmt->fetchColumn();
// Cache i 5 minuter
apcu_store($key, $count, 300);
}
return $count;
}
// Filbaserad cache för komplexare data
function getCachedPosts(): array {
$cacheFile = '/tmp/posts_cache.json';
$cacheTime = 600; // 10 minuter
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
return json_decode(file_get_contents($cacheFile), true);
}
// Hämta från databas
$posts = fetchPostsFromDatabase();
file_put_contents($cacheFile, json_encode($posts));
return $posts;
}
?>
3. OPcache och autoloading:
<?php
// php.ini inställningar för OPcache
/*
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
*/
// Composer autoloader för effektiv class loading
require_once 'vendor/autoload.php';
// Lazy loading av tunga objekt
class Application {
private ?DatabaseConnection $db = null;
public function getDatabase(): DatabaseConnection {
if ($this->db === null) {
$this->db = new DatabaseConnection();
}
return $this->db;
}
}
?>
4. Memory och I/O optimering:
<?php
// Undvik minneskrävande operationer
// Istället för att ladda alla rader:
// $allUsers = $stmt->fetchAll(); // ❌ Kan använda mycket minne
// Använd generators för stora dataset:
function getUsersGenerator(PDO $pdo): Generator {
$stmt = $pdo->query("SELECT * FROM users");
while ($row = $stmt->fetch()) {
yield $row;
}
}
// Buffra output för bättre prestanda
ob_start();
foreach (getUsersGenerator($pdo) as $user) {
echo "<div>" . htmlspecialchars($user['username']) . "</div>";
}
ob_end_flush();
?>
Prestanda-tips:
- Använd OPcache för att cacha kompilerad PHP-kod
- Implementera databas-indexering på ofta använda kolumner
- Cacha dyra operationer (API-anrop, komplexa beräkningar)
- Använd generators för stora dataset
- Minimera databas-queries med eager loading
- Komprimera CSS/JS och använd CDN för statiska tillgångar
Intervjutips: Diskutera olika nivåer av optimering - från kod-nivå till infrastruktur (load balancers, database replicas etc.).
Sammanfattning
Dessa frågor täcker de viktigaste aspekterna av fullstack-utveckling med PHP:
- Grundläggande PHP: Syntax, variabler, funktioner
- Datastrukturer: Arrays, objekt, klasser
- Databaser: SQL, PDO, prepared statements
- Säkerhet: XSS, CSRF, lösenordshantering, filuppladdning
- Arkitektur: CRUD, error handling, prestanda
Förberedelse-tips:
- Öva på att skriva PHP-kod utan IDE-hjälp
- Memorera vanliga säkerhetsprinciper och best practices
- Förstå skillnaden mellan server-side och client-side kod
- Repetera SQL-grunderna och JOIN-operationer
- Var beredd att diskutera prestanda-optimeringar