Inicio / PHP / PHP Profesional: De Intermedio a Avanzado / Preguntas de Entrevista: PHP

Preguntas de Entrevista: PHP

== vs ===, abstract vs interface, type juggling, late static binding, closures y DI.

Avanzado
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Preguntas de Entrevista: PHP

Esta lección cubre las preguntas más frecuentes en entrevistas técnicas para desarrolladores PHP. Cada respuesta incluye explicación, código de ejemplo y las trampas más comunes.


1. ¿Cuál es la diferencia entre == y ===?

== compara solo el valor (con conversión de tipos), mientras que === compara valor y tipo (estricto).

// == hace conversión de tipos (type juggling)
var_dump(0 == 'foo');      // false (PHP 8+), true en PHP 7
var_dump('' == false);     // true
var_dump(0 == false);      // true
var_dump(null == false);   // true
var_dump('1' == true);     // true
var_dump(100 == '1e2');    // true (notación científica)

// === no convierte tipos
var_dump(0 === false);     // false
var_dump('' === false);    // false
var_dump(null === false);  // false
var_dump('1' === 1);       // false

Regla práctica

// ✅ Siempre usa === salvo que tengas una razón específica
if ($valor === null) { /* ... */ }

// Especialmente importante con in_array y array_search
$numeros = [0, 1, 2, 3];
var_dump(in_array('abc', $numeros));          // true (!) sin strict
var_dump(in_array('abc', $numeros, true));    // false ✅ con strict

Respuesta en entrevista: "== realiza comparación flexible con conversión automática de tipos, === compara valor y tipo estrictamente. Siempre usar === para evitar bugs sutiles por type juggling."


2. ¿Cuál es la diferencia entre abstract class e interface?

// ABSTRACT CLASS: puede tener implementación parcial
abstract class Vehiculo
{
    protected int $velocidad = 0;

    // Método concreto con implementación
    public function acelerar(int $incremento): void
    {
        $this->velocidad += $incremento;
    }

    // Método abstracto: las clases hijas DEBEN implementar
    abstract public function tipoMotor(): string;
}

// INTERFACE: solo define el contrato, sin implementación
interface Conducible
{
    public function arrancar(): void;
    public function frenar(): void;
}

// Una clase puede extender UNA sola clase abstracta
// pero implementar MÚLTIPLES interfaces
class Auto extends Vehiculo implements Conducible, Serializable
{
    public function tipoMotor(): string { return 'combustión'; }
    public function arrancar(): void { /* ... */ }
    public function frenar(): void { /* ... */ }
    // ...
}
Característica Abstract Class Interface
Herencia múltiple No (una sola) Sí (múltiples)
Propiedades No (solo constantes)
Métodos con cuerpo Sí (PHP 8.0, default)
Constructor No
Visibilidad Todas Solo public

3. ¿Cómo resuelves conflictos de traits?

Los traits permiten reutilizar código en múltiples clases sin herencia. Cuando dos traits tienen un método con el mismo nombre, hay conflicto.

trait Loggeable
{
    public function registrar(): string
    {
        return 'Registrando en log...';
    }
}

trait Auditable
{
    public function registrar(): string
    {
        return 'Registrando auditoría...';
    }
}

class Servicio
{
    use Loggeable, Auditable {
        // Resolver conflicto: elegir cuál usar
        Loggeable::registrar insteadof Auditable;

        // Crear alias para el otro
        Auditable::registrar as registrarAuditoria;
    }
}

$servicio = new Servicio();
echo $servicio->registrar();            // 'Registrando en log...'
echo $servicio->registrarAuditoria();   // 'Registrando auditoría...'

Cambiar visibilidad con traits

class OtroServicio
{
    use Loggeable {
        registrar as private registrarPrivado;
    }
}

4. ¿Qué es Type Juggling y cómo evitarlo?

Type Juggling es la conversión automática de tipos que PHP realiza en ciertas operaciones.

// Ejemplos peligrosos de type juggling
var_dump('0e12345' == '0e99999');  // true (ambos se interpretan como 0 en notación científica)
var_dump(0 == 'texto');            // false en PHP 8, true en PHP 7
var_dump(null == 0);               // true (!)
var_dump([] == false);             // true
var_dump('' == null);              // true

// Cómo evitarlo
// 1. Usar comparación estricta ===
if ($input === '0') { /* exactamente el string '0' */ }

// 2. Usar declare(strict_types=1)
declare(strict_types=1);

function sumar(int $a, int $b): int
{
    return $a + $b;
}

sumar('5', '3');  // TypeError en strict_types=1
                  // Retornaría 8 sin strict_types

// 3. Usar funciones de tipo
$esVacio = $valor === '' || $valor === null;
// En vez de: empty($valor) — que convierte tipos implícitamente

Tip de entrevista: Menciona siempre declare(strict_types=1) y explica que lo usas al inicio de cada archivo PHP.


5. ¿Qué es Late Static Binding?

Late Static Binding (LSB) resuelve la clase en tiempo de ejecución usando static:: en lugar de self::.

class ModeloBase
{
    protected static string $tabla = 'base';

    public static function obtenerTabla(): string
    {
        return static::$tabla;  // Resuelve en tiempo de ejecución
    }

    public static function obtenerTablaSelf(): string
    {
        return self::$tabla;    // Siempre resuelve a ModeloBase
    }

    public static function crear(array $datos): static
    {
        // `static` como tipo de retorno = la clase real que llama
        $instancia = new static();
        // ... configurar
        return $instancia;
    }
}

class Usuario extends ModeloBase
{
    protected static string $tabla = 'usuarios';
}

class Producto extends ModeloBase
{
    protected static string $tabla = 'productos';
}

echo Usuario::obtenerTabla();      // 'usuarios' (static::)
echo Usuario::obtenerTablaSelf();  // 'base' (self::)
echo Producto::obtenerTabla();     // 'productos'

$usuario = Usuario::crear([]);     // Instancia de Usuario, no de ModeloBase

Respuesta clave: "self:: se resuelve en la clase donde se define el método. static:: se resuelve en la clase que realmente invoca el método (Late Static Binding)."


6. Closures y funciones anónimas

// Closure básica
$saludar = function (string $nombre): string {
    return "Hola, {$nombre}";
};

echo $saludar('Ana'); // Hola, Ana

// Closure con `use` para capturar variables externas
$iva = 0.21;
$calcularPrecio = function (float $precio) use ($iva): float {
    return $precio * (1 + $iva);
};

echo $calcularPrecio(100); // 121.0

// Captura por referencia
$contador = 0;
$incrementar = function () use (&$contador): void {
    $contador++;
};
$incrementar();
$incrementar();
echo $contador; // 2

// Arrow functions (PHP 7.4+): capturan variables automáticamente
$multiplicador = fn(float $x) => $x * $iva;
echo $multiplicador(200); // 42.0

Closures como callbacks

$usuarios = [
    ['nombre' => 'Ana', 'edad' => 25],
    ['nombre' => 'Juan', 'edad' => 30],
    ['nombre' => 'Pedro', 'edad' => 20],
];

// Ordenar por edad
usort($usuarios, fn($a, $b) => $a['edad'] <=> $b['edad']);

// Filtrar mayores de edad
$mayores = array_filter($usuarios, fn($u) => $u['edad'] >= 18);

// Transformar
$nombres = array_map(fn($u) => $u['nombre'], $usuarios);

7. ¿Qué es PSR-4 y cómo funciona el autoloading?

PSR-4 es el estándar de autoloading que mapea namespaces a directorios del sistema de archivos.

// composer.json
// {
//     "autoload": {
//         "psr-4": {
//             "App\\": "src/"
//         }
//     }
// }

// Archivo: src/Servicios/EmailService.php
namespace App\Servicios;

class EmailService
{
    public function enviar(string $para, string $asunto): bool
    {
        // Lógica de envío
        return true;
    }
}

// Archivo: src/Controladores/UsuarioController.php
namespace App\Controladores;

use App\Servicios\EmailService; // Autoloading lo carga automáticamente

class UsuarioController
{
    public function __construct(
        private EmailService $emailService,
    ) {}
}

Respuesta de entrevista: "PSR-4 define una convención donde cada namespace raíz se mapea a un directorio base. Composer genera un autoloader optimizado que carga las clases automáticamente cuando se referencian. Usar composer dump-autoload -o en producción para optimizar."


8. ¿Qué es la Inyección de Dependencias?

La Inyección de Dependencias (DI) pasa las dependencias a una clase en lugar de crearlas internamente.

// ❌ MALO: Dependencia acoplada
class ServicioNotificacion
{
    public function notificar(string $mensaje): void
    {
        $mailer = new SmtpMailer(); // Acoplamiento fuerte
        $mailer->enviar($mensaje);
    }
}

// ✅ BIEN: Inyección por constructor
interface Mailer
{
    public function enviar(string $destino, string $mensaje): bool;
}

class SmtpMailer implements Mailer
{
    public function enviar(string $destino, string $mensaje): bool
    {
        // Enviar por SMTP
        return true;
    }
}

class ServicioNotificacion
{
    public function __construct(
        private readonly Mailer $mailer,  // Depende de una abstracción
    ) {}

    public function notificar(string $destino, string $mensaje): bool
    {
        return $this->mailer->enviar($destino, $mensaje);
    }
}

// Fácil de testear con un mock
$mockMailer = new class implements Mailer {
    public array $enviados = [];
    public function enviar(string $destino, string $mensaje): bool
    {
        $this->enviados[] = [$destino, $mensaje];
        return true;
    }
};

$servicio = new ServicioNotificacion($mockMailer);
$servicio->notificar('ana@test.com', 'Hola');
assert(count($mockMailer->enviados) === 1);

9. Patrones de diseño más preguntados

Singleton

class Configuracion
{
    private static ?self $instancia = null;
    private array $datos = [];

    private function __construct() {}         // Evitar new
    private function __clone() {}             // Evitar clone
    public function __wakeup() { throw new \Exception('No deserializable'); }

    public static function obtener(): self
    {
        if (self::$instancia === null) {
            self::$instancia = new self();
        }
        return self::$instancia;
    }

    public function set(string $clave, mixed $valor): void { $this->datos[$clave] = $valor; }
    public function get(string $clave): mixed { return $this->datos[$clave] ?? null; }
}

Strategy

interface EstrategiaPago
{
    public function procesar(float $monto): bool;
}

class PagoTarjeta implements EstrategiaPago
{
    public function procesar(float $monto): bool
    {
        echo "Procesando ${$monto} con tarjeta" . PHP_EOL;
        return true;
    }
}

class PagoPaypal implements EstrategiaPago
{
    public function procesar(float $monto): bool
    {
        echo "Procesando ${$monto} con PayPal" . PHP_EOL;
        return true;
    }
}

class ProcesadorPago
{
    public function __construct(private EstrategiaPago $estrategia) {}

    public function cobrar(float $monto): bool
    {
        return $this->estrategia->procesar($monto);
    }
}

// Uso: intercambiable en tiempo de ejecución
$procesador = new ProcesadorPago(new PagoPaypal());
$procesador->cobrar(99.99);

Observer

class EventDispatcher
{
    /** @var array<string, list<Closure>> */
    private array $listeners = [];

    public function escuchar(string $evento, Closure $callback): void
    {
        $this->listeners[$evento][] = $callback;
    }

    public function despachar(string $evento, mixed $datos = null): void
    {
        foreach ($this->listeners[$evento] ?? [] as $listener) {
            $listener($datos);
        }
    }
}

$dispatcher = new EventDispatcher();
$dispatcher->escuchar('usuario.creado', fn($u) => enviarEmailBienvenida($u));
$dispatcher->escuchar('usuario.creado', fn($u) => registrarEnAnalytics($u));
$dispatcher->despachar('usuario.creado', $nuevoUsuario);

10. Características clave de PHP 8 para entrevistas

Preguntas rápidas que deberías poder responder de inmediato:

// Named arguments
array_fill(start_index: 0, count: 5, value: 'x');

// Match expression
$resultado = match($tipo) {
    'a', 'b' => 'grupo 1',
    'c' => 'grupo 2',
    default => 'otro',
};

// Nullsafe operator
$nombre = $usuario?->perfil?->nombre ?? 'Anónimo';

// Enums
enum Color: string {
    case Rojo = '#FF0000';
    case Verde = '#00FF00';
}

// Constructor promotion + readonly
class DTO {
    public function __construct(
        public readonly string $nombre,
        public readonly int $edad,
    ) {}
}

// Fibers
$fiber = new Fiber(fn() => Fiber::suspend('hola'));
echo $fiber->start(); // 'hola'

// First-class callables
$fn = strtoupper(...);
echo $fn('hola'); // 'HOLA'

// Union types
function procesar(int|string $valor): string|false { /* ... */ }

// Intersection types
function aceptar(Countable&Iterator $col): void { /* ... */ }

// #[Override]
class Hijo extends Padre {
    #[\Override]
    public function metodo(): void {}
}

Preguntas trampa comunes

// ¿Qué imprime esto?
echo '1' + '2';           // 3 (int)
echo '1' . '2';           // '12' (string)
echo true + true;         // 2
echo 'php' + 1;           // 1
echo '5abc' + 0;          // 5 (PHP 7), Warning + 5 (PHP 8)

// ¿Diferencia entre echo y print?
echo 'hola', 'mundo';  // echo acepta múltiples argumentos
print('hola');          // print retorna 1 (se puede usar en expresiones)

// ¿Qué retorna esto?
var_dump(0.1 + 0.2 == 0.3);   // false (punto flotante IEEE 754)
var_dump(round(0.1 + 0.2, 1) === 0.3); // true

// ¿Diferencia entre isset, empty y is_null?
$x = '';
var_dump(isset($x));    // true (existe y no es null)
var_dump(empty($x));    // true (es '', 0, null, false, [])
var_dump(is_null($x));  // false (no es null)

Resumen: lo que el entrevistador busca

Área Lo que demuestra
== vs === Entiendes las trampas del lenguaje
Abstract/Interface Conoces OOP sólidamente
Late Static Binding Dominas herencia avanzada
DI y patrones Escribes código mantenible y testeable
PSR-4 Conoces el ecosistema y estándares
PHP 8 features Estás actualizado y usas el lenguaje moderno
Type Juggling Escribes código robusto y seguro

Consejo final: No solo memorices respuestas. Practica escribiendo código que use estos conceptos. Los mejores entrevistadores piden que escribas código en vivo o expliques tu razonamiento paso a paso.

🔒

Ejercicio práctico disponible

Algoritmos y conceptos de entrevista PHP

Desbloquear ejercicios
// Algoritmos y conceptos de entrevista PHP
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes