Conceptos de Clean Code adaptados para PHP

Overview

Clean Code PHP

Traducción al español de Clean Code PHP realizado por Jupeter. Si encuentras un error de ortografía, de redacción o de traducción; no dudes en hacer un PR.

Tabla de contenidos

  1. Introducción
  2. Variables
  3. Funciones
  4. Objetos y estructuras de datos
  5. Clases
  6. SOLID
  7. No te repitas
  8. Traducciones

Introducción

Adaptación para PHP de los principios de ingeniería de software descritos por Robert C. Martin en su libro Clean Code. Esta no es una guía de estilo. Es una guía para producir software que sea legible, reutilizable y refactorizable en PHP.

No todos los principios deben ser seguidos estrictamente, e incluso unos pocos serán aceptados totalmente. Estos son una referencia y nada más, pero han sido desarrollados tras los años de experiencia del autor de Clean Code.

Inspirado por clean-code-javascript

A pesar de que muchos desarrolladores aún utilizan PHP 5, la mayoría de los ejemplos en este artículo funcionan sólo en PHP 7.1+.

Variables

Usar variables que tengan significado y sean pronunciables

Mal:

$ymdstr = $moment->format('y-m-d');

Bien:

$currentDate = $moment->format('y-m-d');

Volver

Usar el mismo vocabulario para el mismo tipo de variable

Mal:

getUserInfo();
getUserData();
getUserRecord();
getUserProfile();

Bien:

getUser();

Volver

Usar nombres que puedan ser buscados (parte 1)

Leerás más código del que puedas escribir. Es importante que el código que escribimos sea legible y que pueda ser buscado. Dañamos a nuestros lectores al no escribir nombres de variables que tengan significado para entender nuestro programa. Crea nombres que puedan ser buscados.

Mal:

// ¿Qué diablos significa 448?
$result = $serializer->serialize($data, 448);

Bien:

$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

Usar nombres que puedan ser buscados (parte 2)

Mal:

// ¿Qué diablos significa 4?
if ($user->access & 4) {
    // ...
}

Bien:

class User
{
    const ACCESS_READ = 1;
    const ACCESS_CREATE = 2;
    const ACCESS_UPDATE = 4;
    const ACCESS_DELETE = 8;
}

if ($user->access & User::ACCESS_UPDATE) {
    // Editar ...
}

Volver

Usar variables explicativas

Mal:

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches[1], $matches[2]);

Nada mal:

Está mejor, pero todavía es muy dependiente de la expresión regular.

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

[, $city, $zipCode] = $matches;
saveCityZipCode($city, $zipCode);

Bien:

Disminuye la dependencia a la expresión regular al nombrar subpatrones.

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(?.+?)\s*(?\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches['city'], $matches['zipCode']);

Volver

Evitar anidación profunda usando return tempranamente (parte 1)

Muchas declaraciones if else pueden hacer tu código difícil de seguir. Explicito es mejor que implícito.

Mal:

function isShopOpen($day): bool
{
    if ($day) {
        if (is_string($day)) {
            $day = strtolower($day);
            if ($day === 'friday') {
                return true;
            } elseif ($day === 'saturday') {
                return true;
            } elseif ($day === 'sunday') {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}

Bien:

function isShopOpen(string $day): bool
{
    if (empty($day)) {
        return false;
    }

    $openingDays = [
        'friday', 'saturday', 'sunday'
    ];

    return in_array(strtolower($day), $openingDays, true);
}

Volver

Evitar anidación profunda usando return tempranamente (parte 2)

Mal:

function fibonacci(int $n)
{
    if ($n < 50) {
        if ($n !== 0) {
            if ($n !== 1) {
                return fibonacci($n - 1) + fibonacci($n - 2);
            } else {
                return 1;
            }
        } else {
            return 0;
        }
    } else {
        return 'Not supported';
    }
}

Bien:

function fibonacci(int $n): int
{
    if ($n === 0 || $n === 1) {
        return $n;
    }

    if ($n > 50) {
        throw new \Exception('Not supported');
    }

    return fibonacci($n - 1) + fibonacci($n - 2);
}

Volver

Evitar mapas mentales

No fuerces al lector de tu código a traducir lo que significa una variable. Explicito es mejor que implícito.

Mal:

$l = ['Austin', 'New York', 'San Francisco'];

for ($i = 0; $i < count($l); $i++) {
    $li = $l[$i];
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    // Espera, ¿Para qué era `$li`?
    dispatch($li);
}

Bien:

$locations = ['Austin', 'New York', 'San Francisco'];

foreach ($locations as $location) {
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    dispatch($location);
}

Volver

No agregar contexto innecesario

Si el nombre de tu clase/objeto te dice algo, no lo repitas en el nombre del atributo.

Mal:

class Car
{
    public $carMake;
    public $carModel;
    public $carColor;

    //...
}

Bien:

class Car
{
    public $make;
    public $model;
    public $color;

    //...
}

Volver

Usar argumentos por defecto en lugar de cortocircuitos o condicionales

Nada bien:

No está bien porque $breweryName puede ser NULL.

function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

Nada mal:

Está opción es más entendible que la versión anterior, pero es mejor controlar el valor de la variable.

function createMicrobrewery($name = null): void
{
    $breweryName = $name ?: 'Hipster Brew Co.';
    // ...
}

Bien:

Si tienes instalado PHP 7+, entonces puedas usar implicación de tipos y así asegurarte de que $breweryName no será NULL.

function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

Volver

Funciones

Argumentos de la función (idealmente 2 o menos)

Limitar la cantidad de parámetros de una función es increíblemente importante porque la hace más fácil de probar. Teniendo más de tres lleva a una explosión de combinaciones que tendrás que probar, argumento por argumento.

El caso ideal es cero argumentos. Uno o dos argumentos están bien, pero tres deben ser evitados. Algo más debe ser dicho. Usualmente, si tienes más de dos argumentos significa que estás intentando hacer demasiado en la función. En los casos en que no, la mayoría del tiempo un objeto de alto nivel será suficiente como argumento.

Mal:

function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void
{
    // ...
}

Bien:

class MenuConfig
{
    public $title;
    public $body;
    public $buttonText;
    public $cancellable = false;
}

$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = true;

function createMenu(MenuConfig $config): void
{
    // ...
}

Volver

Las funciones deben hacer una cosa

Esta es por lejos una de las más importantes reglas en ingeniería de software. Cuando las funciones hacen más de una cosa, se vuelven difíciles de hacer, probar y razonar sobre ellas. Cuando puedes aislar una función en una sola acción, ellas pueden ser refactorizadas con facilidad y tu código será mucho más limpio de leer. Si esta es la única regla que sigas de esta guía, estarás por sobre muchos desarrolladores.

Mal:

function emailClients(array $clients): void
{
    foreach ($clients as $client) {
        $clientRecord = $db->find($client);
        if ($clientRecord->isActive()) {
            email($client);
        }
    }
}

Bien:

function emailClients(array $clients): void
{
    $activeClients = activeClients($clients);
    array_walk($activeClients, 'email');
}

function activeClients(array $clients): array
{
    return array_filter($clients, 'isClientActive');
}

function isClientActive(int $client): bool
{
    $clientRecord = $db->find($client);

    return $clientRecord->isActive();
}

Volver

Los nombres de las funciones deben indicar lo que hacen

Mal:

class Email
{
    //...

    public function handle(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// ¿Qué es esto? ¿Un manejador para los mensajes? ¿Ahora escribimos en un archivo?
$message->handle();

Bien:

class Email 
{
    //...

    public function send(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// Limpio y obvio
$message->send();

Volver

Las funciones deben tener sólo un nivel de abstracción

Cuando tienes más de un nivel de abstracción usualmente es porque tu función está haciendo demasiado. Separarlas en funciones lleva a la reutilización y facilita las pruebas.

Mal:

function parseBetterJSAlternative(string $code): void
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            // ...
        }
    }

    $ast = [];
    foreach ($tokens as $token) {
        // lex...
    }

    foreach ($ast as $node) {
        // convertir...
    }
}

También mal:

Hemos separado algunas de las funcionalidades, pero la función parseBetterJSAlternative() todavía es muy compleja e imposible de probar.

function tokenize(string $code): array
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            $tokens[] = /* ... */;
        }
    }

    return $tokens;
}

function lexer(array $tokens): array
{
    $ast = [];
    foreach ($tokens as $token) {
        $ast[] = /* ... */;
    }

    return $ast;
}

function parseBetterJSAlternative(string $code): void
{
    $tokens = tokenize($code);
    $ast = lexer($tokens);
    foreach ($ast as $node) {
        // convertir...
    }
}

Bien:

Lo mejor es sacar las dependencias de la función parseBetterJSAlternative().

class Tokenizer
{
    public function tokenize(string $code): array
    {
        $regexes = [
            // ...
        ];

        $statements = explode(' ', $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }

        return $tokens;
    }
}

class Lexer
{
    public function lexify(array $tokens): array
    {
        $ast = [];
        foreach ($tokens as $token) {
            $ast[] = /* ... */;
        }

        return $ast;
    }
}

class BetterJSAlternative
{
    private $tokenizer;
    private $lexer;

    public function __construct(Tokenizer $tokenizer, Lexer $lexer)
    {
        $this->tokenizer = $tokenizer;
        $this->lexer = $lexer;
    }

    public function parse(string $code): void
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // convertir...
        }
    }
}

Volver

No usar banderas como parámetros de funciones

Las banderas le dicen al usuario que la función hace más de una cosa. Las funciones deben hacer sólo una. Divide tus funciones si ellas siguen diferentes caminos basados en un valor booleano.

Mal:

function createFile(string $name, bool $temp = false): void
{
    if ($temp) {
        touch('./temp/'.$name);
    } else {
        touch($name);
    }
}

Bien:

function createFile(string $name): void
{
    touch($name);
}

function createTempFile(string $name): void
{
    touch('./temp/'.$name);
}

Volver

Evitar efectos secundarios

Una función produce un efecto secundario si hace algo más que tomar un valor y devolver otros. Un efecto secundario puede ser escribir en un archivo, modificar alguna variable global, o accidentalmente darle todo tu dinero a un extraño.

Ahora, ocasionalmente necesitaras los efectos secundarios en un programa. Como los ejemplos anteriores, necesitarás escribir en un archivo. Lo que quieres hacer en esos casos es centralizar donde realizarlos. No tengas muchas funciones y clases que escriban un archivo en particular. Ten un servicio que lo haga. Uno y sólo uno.

El punto principal es evitar trampas comunes como compartir estados entre objetos sin alguna estructura, usar tipos de datos mutables que puedan ser escritos por cualquiera, y no centralizar donde el efecto paralelo ocurre. Si puedes hacerlo, serás más feliz que la vasta mayoría de los demás programadores.

Mal:

// Variable global referenciada por la siguiente función.
// Si tenemos otra función que use el mismo nombre, ahora será un arreglo y podría romperla.
$name = 'Ryan McDermott';

function splitIntoFirstAndLastName(): void
{
    global $name;

    $name = explode(' ', $name);
}

splitIntoFirstAndLastName();

var_dump($name); // ['Ryan', 'McDermott'];

Bien:

function splitIntoFirstAndLastName(string $name): array
{
    return explode(' ', $name);
}

$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);

var_dump($name); // 'Ryan McDermott';
var_dump($newName); // ['Ryan', 'McDermott'];

Volver

No escribir funciones globales

Contaminar los globales es una mala práctica en muchos lenguajes porque puedes chocar con otra librería y el usuario de tu API podría no enterarse hasta obtener una excepción en producción. Pensemos en un ejemplo: qué pasaría si esperabas tener un arreglo de configuración. Podrías escribir una función global como config(), pero podría chocar con otra librería que haya intentado hacer lo mismo.

Mal:

function config(): array
{
    return  [
        'foo' => 'bar',
    ]
}

Bien:

class Configuration
{
    private $configuration = [];

    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }

    public function get(string $key): ?string
    {
        return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
    }
}

Crea la variable $configuration con una instancia de la clase Configuration

$configuration = new Configuration([
    'foo' => 'bar',
]);

Y ahora puedes usar una instancia de la clase Configuration en tu aplicación.

Volver

No usar el patrón Singleton

Singleton es un anti-patrón. Citando a Brian Button:

  1. Son usados generalmente como una instancia global, ¿Por qué eso es malo? Porque escondes las dependencias de tu aplicación en tu código, en lugar de exponerlas mediante interfaces. Hacer algo global para evitar pasarlo es una hediondez de código.
  2. Violan el principio de la responsabilidad única: en virtud del hecho de que ellos controlan su propia creación y ciclo de vida.
  3. Inherentemente causan que el código esté estrechamente acoplado. Esto hace que muchas veces sean difíciles de probar.
  4. Llevan su estado al ciclo de vida de la aplicación. Otro golpe a las pruebas porque puedes terminar con una situación donde las pruebas necesitan ser ordenadas lo cual es un gran no para las pruebas unitarias. ¿Por qué? Porque cada prueba unitaria debe hacerse independiente de la otra.

Misko Hevery ha realizado unas reflexiones interesantes sobre el origen del problema.

Mal:

class DBConnection
{
    private static $instance;

    private function __construct(string $dsn)
    {
        // ...
    }

    public static function getInstance(): DBConnection
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    // ...
}

$singleton = DBConnection::getInstance();

Bien:

class DBConnection
{
    public function __construct(string $dsn)
    {
        // ...
    }

     // ...
}

Crea una instancia de la clase DBConnection y configúrala con DSN.

$connection = new DBConnection($dsn);

Y ahora debes usar la instancia de DBConnection en tu aplicación.

Volver

Encapsular condicionales

Mal:

if ($article->state === 'published') {
    // ...
}

Bien:

if ($article->isPublished()) {
    // ...
}

Volver

Evitar condicionales negativos

Mal:

function isDOMNodeNotPresent(\DOMNode $node): bool
{
    // ...
}

if (!isDOMNodeNotPresent($node))
{
    // ...
}

Bien:

function isDOMNodePresent(\DOMNode $node): bool
{
    // ...
}

if (isDOMNodePresent($node)) {
    // ...
}

Volver

Evitar condicionales

Esta parece ser una tarea imposible. Al escuchar esto por primera vez, la mayoría de la gente dice, "¿cómo se supone que haré algo sin una declaración if?" La respuesta es que la mayoría de las veces puedes usar polimorfismo para lograr el mismo resultado.

La segunda pregunta usualmente es, "bien, eso es genial, ¿pero cómo puedo hacerlo?" La respuesta es un concepto de código limpio que ya hemos aprendido: una función debe hacer sólo una cosa. Cuando tienes clases y funciones que usan declaraciones if, estás diciéndole al usuario que tu función hace más de una cosa. Recuerda, hacer sólo una cosa.

Mal:

class Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        switch ($this->type) {
            case '777':
                return $this->getMaxAltitude() - $this->getPassengerCount();
            case 'Air Force One':
                return $this->getMaxAltitude();
            case 'Cessna':
                return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
    }
}

Bien:

interface Airplane
{
    // ...

    public function getCruisingAltitude(): int;
}

class Boeing777 implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getPassengerCount();
    }
}

class AirForceOne implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude();
    }
}

class Cessna implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
    }
}

Volver

Evitar revisión de tipo (parte 1)

PHP es un lenguaje no tipado, lo que quiere decir que tus funciones pueden tener cualquier tipo de argumento. Algunas veces habrás sentido esta libertad y te habrás tentado a hacer revisión de tipo en tus funciones. Hay muchas maneras de evitar tener que hacerlo. Lo primero es considerar la consistencia de las APIs.

Mal:

function travelToTexas($vehicle): void
{
    if ($vehicle instanceof Bicycle) {
        $vehicle->pedalTo(new Location('texas'));
    } elseif ($vehicle instanceof Car) {
        $vehicle->driveTo(new Location('texas'));
    }
}

Bien:

function travelToTexas(Traveler $vehicle): void
{
    $vehicle->travelTo(new Location('texas'));
}

Volver

Evitar revisión de tipo (parte 2)

Si estás trabajando con valores básicos primitivos como cadenas, enteros y arreglos; y estás usando PHP 7+ y no puedes usar polimorfismo pero aún necesitas realizar una revisión de tipo entonces puedes considerar declaración de tipo o modo estricto. Esto provee tipado estático sobre la sintaxis estándar de PHP. El problema con hacer la revisión de tipo manualmente es que requerirá escribir texto adicional que dará una falsa "seguridad de tipado" que no compensará la perdida de legibilidad. Mantén tu código PHP limpio, escribe buenas pruebas, y realiza buenas revisiones. Dicho de otra forma, realiza todo lo que se ha recomendado pero usando la declaración de tipo estricto en PHP o el modo estricto.

Mal:

function combine($val1, $val2): int
{
    if (!is_numeric($val1) || !is_numeric($val2)) {
        throw new \Exception('Must be of type Number');
    }

    return $val1 + $val2;
}

Bien:

function combine(int $val1, int $val2): int
{
    return $val1 + $val2;
}

Volver

Quitar código muerto

El código muerto es tan malo como el código duplicado. No hay motivos para mantenerlo en tu código fuente. Si no está siendo llamado, ¡deshazte del! Siempre estará a salvo en tu versión histórica si aún lo necesitas.

Mal:

function oldRequestModule(string $url): void
{
    // ...
}

function newRequestModule(string $url): void
{
    // ...
}

$request = newRequestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

Bien:

function requestModule(string $url): void
{
    // ...
}

$request = requestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

Volver

Objetos y estructuras de datos

Usar encapsulación de objetos

En PHP puedes establecer métodos como public, protected y private. Al utilizarlos, puedes controlar las modificaciones a las propiedades de un objeto.

  • Cuando quieras hacer algo más que obtener una propiedad de un objeto, no tienes que revisar y cambiar cada método de acceso en tu código fuente.
  • Agrega una validación simple cuando haces set.
  • Encapsula la representación interna.
  • Facilita agregar registro y manejo de errores al obtener y colocar.
  • Puedes sobrescribir la funcionalidad por defecto al heredar la clase.
  • Puedes hacer carga diferida de las propiedades del objeto, por ejemplo, al obtenerlos desde un servidor.

Adicionalmente, esto es parte del principio abierto/cerrado.

Mal:

class BankAccount
{
    public $balance = 1000;
}

$bankAccount = new BankAccount();

// Comprar zapatos...
$bankAccount->balance -= 100;

Bien:

class BankAccount
{
    private $balance;

    public function __construct(int $balance = 1000)
    {
      $this->balance = $balance;
    }

    public function withdraw(int $amount): void
    {
        if ($amount > $this->balance) {
            throw new \Exception('Amount greater than available balance.');
        }

        $this->balance -= $amount;
    }

    public function deposit(int $amount): void
    {
        $this->balance += $amount;
    }

    public function getBalance(): int
    {
        return $this->balance;
    }
}

$bankAccount = new BankAccount();

// Comprar zapatos...
$bankAccount->withdraw($shoesPrice);

// Obtener saldo
$balance = $bankAccount->getBalance();

Volver

Hacer que los objetos tengan partes private/protected

  • Los métodos y propiedades configuradas como public son las más expuestas a los cambios, porque algún código externo puede fácilmente modificarlos pero no tienes control sobre qué es lo que se está modificando en verdad. Modificaciones en clases son peligrosas para usuarios de una clase.
  • El modificador protected es tan peligroso como el public, porque están disponible en el ámbito de cualquier clase hija. Efectivamente esto significa que la diferencia entre public y protected es el mecanismo de acceso, pero la encapsulación garantiza que siga siendo la misma. Modificaciones en clases son peligrosas para todas las clases descendientes.
  • El modificador private garantiza que el código es peligroso de modificar sólo en los límites de una clase en particular (estarás asegurado ante modificaciones y no tendrás un efecto Jenga).

Por lo tanto, usar private por defecto y public/protected cuando quieras proveer acceso a clases externas.

Para más información, puedes leer el siguiente articulo escrito por Fabien Potencier.

Mal:

class Employee
{
    public $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->name; // Nombre del empleado: John Doe

Bien:

class Employee
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->getName(); // Nombre del empleado: John Doe

Volver

Clases

Preferir composición antes que herencia

Como se ha dicho en Design Patterns de the Gang of Four, debes preferir composición sobre herencia cuando puedas hacerlo. Hay muchas buenas razones para usar herencia y muchas buenas razones para usar composición. El punto principal de esta máxima es que si tu mente instintivamente va hacia herencia, trata de pensar si la composición modela tu problema de mejor manera. En algunos casos si es posible.

Debes preguntarte entonces, "¿cuándo debo usar herencia?" Eso depende del problema que tengas entre manos, pero hay una lista decente que indica cuando tiene más sentido usar herencia en lugar de composición.

  1. Herencia representa una relación "es-un" y no una relación "tiene-un". (Humano->Animal vs. Usuario->DetallesDeUsuario).
  2. Puedes reutilizar código desde las clases padres. (Humanos pueden moverse como todos los animales).
  3. Puedes querer hacer cambios globales en las clases derivadas al cambiar una clase padre. (Cambiar el consumo de calorías de todos los animales cuando se mueven)

Mal:

ssn = $ssn; $this->salary = $salary; } // ... }">
class Employee 
{
    private $name;
    private $email;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }

    // ...
}

// Está mal porque los empleados "tienen" información de impuestos.
// EmployeeTaxData no es un tipo de Employee

class EmployeeTaxData extends Employee 
{
    private $ssn;
    private $salary;
    
    public function __construct(string $name, string $email, string $ssn, string $salary)
    {
        parent::__construct($name, $email);

        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

Bien:

class EmployeeTaxData 
{
    private $ssn;
    private $salary;

    public function __construct(string $ssn, string $salary)
    {
        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

class Employee 
{
    private $name;
    private $email;
    private $taxData;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }

    public function setTaxData(string $ssn, string $salary)
    {
        $this->taxData = new EmployeeTaxData($ssn, $salary);
    }

    // ...
}

Volver

Evitar interfaces fluidas

Una interfaz fluida es una API orientada a objetos que ayuda a mejorar la legibilidad del código fuente al usar encadenamientos de métodos.

Pueden haber algunos contextos, frecuentemente los constructores de objetos, donde este patrón reduce la cantidad de texto del código (por ejemplo el Constructor de Simulación de PHPUnit o Constructor de Consultas Doctrine), pero a menudo esto viene con algunos costos:

  1. Rompe la encapsulación
  2. Rompe los decoradores
  3. Es difícil de simular en un conjunto de pruebas.
  4. Dificulta la lectura de los diffs de commits.

Para más información, puedes leer el articulo completo escrito por Marco Pivetta.

Mal:

class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';

    public function setMake(string $make): self
    {
        $this->make = $make;

        // Observación: Retornará esto para encadenar.
        return $this;
    }

    public function setModel(string $model): self
    {
        $this->model = $model;

        // Observación: Retornará esto para encadenar.
        return $this;
    }

    public function setColor(string $color): self
    {
        $this->color = $color;

        // Observación: Retornará esto para encadenar.
        return $this;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = (new Car())
  ->setColor('pink')
  ->setMake('Ford')
  ->setModel('F-150')
  ->dump();

Bien:

class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';

    public function setMake(string $make): void
    {
        $this->make = $make;
    }

    public function setModel(string $model): void
    {
        $this->model = $model;
    }

    public function setColor(string $color): void
    {
        $this->color = $color;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();

Volver

SOLID

SOLID es un acrónimo mnemotécnico creado por Michael Feathers para los primeros cinco principios nombrados por Robert Martin, lo que significa los cinco principios básicos de la programación y diseño orientado a objetos.

Principio de responsabilidad única

Como se ha dicho en Clean Code, "No debe haber nunca más de un motivo para que una clase cambie". Es tentador empaquetar una clase con muchas funcionalidades, como si sólo pudieras llevar una maleta en tu vuelo. El problema es que esa clase no será conceptualmente cohesiva y te dará muchas razones para cambiarla. Minimizar la cantidad de veces que necesitas realizar cambios a una clase es importante. Es importante porque hay demasiadas funcionalidades en una sola clase y modificas una parte de ella, será difícil entender cómo afectará a otros módulos que dependan de ella en tu código fuente.

Mal:

class UserSettings
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function changeSettings(array $settings): void
    {
        if ($this->verifyCredentials()) {
            // ...
        }
    }

    private function verifyCredentials(): bool
    {
        // ...
    }
}

Bien:

class UserAuth 
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
    
    public function verifyCredentials(): bool
    {
        // ...
    }
}

class UserSettings 
{
    private $user;
    private $auth;

    public function __construct(User $user) 
    {
        $this->user = $user;
        $this->auth = new UserAuth($user);
    }

    public function changeSettings(array $settings): void
    {
        if ($this->auth->verifyCredentials()) {
            // ...
        }
    }
}

Volver

Principio de abierto/cerrado

Como dijo Bertrand Meyer, "las entidades de software (clases, módulos, funciones, etc) deben ser abiertas para ser extendidas, pero cerradas para modificarlas." ¿Qué significa esto? Este principio establece básicamente que puedes permitir a los usuarios agregar nuevas funcionalidades pero sin cambiar el código existente.

Mal:

abstract class Adapter
{
    protected $name;

    public function getName(): string
    {
        return $this->name;
    }
}

class AjaxAdapter extends Adapter
{
    public function __construct()
    {
        parent::__construct();

        $this->name = 'ajaxAdapter';
    }
}

class NodeAdapter extends Adapter
{
    public function __construct()
    {
        parent::__construct();

        $this->name = 'nodeAdapter';
    }
}

class HttpRequester
{
    private $adapter;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
    }

    public function fetch(string $url): Promise
    {
        $adapterName = $this->adapter->getName();

        if ($adapterName === 'ajaxAdapter') {
            return $this->makeAjaxCall($url);
        } elseif ($adapterName === 'httpNodeAdapter') {
            return $this->makeHttpCall($url);
        }
    }

    private function makeAjaxCall(string $url): Promise
    {
        // Solicitar y retornar una promesa
    }

    private function makeHttpCall(string $url): Promise
    {
        // Solicitar y retornar una promesa
    }
}

Bien:

interface Adapter
{
    public function request(string $url): Promise;
}

class AjaxAdapter implements Adapter
{
    public function request(string $url): Promise
    {
        // Solicitar y retornar una promesa
    }
}

class NodeAdapter implements Adapter
{
    public function request(string $url): Promise
    {
        // Solicitar y retornar una promesa
    }
}

class HttpRequester
{
    private $adapter;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
    }

    public function fetch(string $url): Promise
    {
        return $this->adapter->request($url);
    }
}

Volver

Principio de la sustitución de Liskov

Este es un término aterrador para un concepto muy simple. Se define formalmente como "Si S es un subtipo de T, entonces los objetos de tipo T pueden ser reemplazados con objetos de tipo S (es decir, objetos de tipo S pueden sustituir objetos de tipo T) sin alterar alguna propiedad deseable de ese programa (exactitud, tarea realizada, etc)." Esa es una definición aún más aterradora.

La mejor explicación para esto es que si tienes una clase padre y una clase hija, entonces la clase padre y la clase hija pueden intercambiarse sin obtener resultados incorrectos. Esto puede ser confuso, así que veamos el clásico ejemplo del Cuadrado-Rectángulo. Matemáticamente, un cuadrado es un Rectángulo, pero si tu modelo está usando la relación "es-un" por herencia, rápidamente estarás en problemas.

Mal:

class Rectangle
{
    protected $width = 0;
    protected $height = 0;

    public function render(int $area): void
    {
        // ...
    }

    public function setWidth(int $width): void
    {
        $this->width = $width;
    }

    public function setHeight(int $height): void
    {
        $this->height = $height;
    }

    public function getArea(): int
    {
        return $this->width * $this->height;
    }
}

class Square extends Rectangle
{
    public function setWidth(int $width): void
    {
        $this->width = $this->height = $width;
    }

    public function setHeight(int $height): void
    {
        $this->width = $this->height = $height;
    }
}

/**
 * @param Rectangle[] $rectangles
 */
function renderLargeRectangles(array $rectangles): void
{
    foreach ($rectangles as $rectangle) {
        $rectangle->setWidth(4);
        $rectangle->setHeight(5);
        $area = $rectangle->getArea(); // MAL: Retornará 25 para cuadrados y debería ser 20
        $rectangle->render($area);
    }
}

$rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($rectangles);

Bien:

abstract class Shape
{
    protected $width = 0;
    protected $height = 0;

    abstract public function getArea(): int;

    public function render(int $area): void
    {
        // ...
    }
}

class Rectangle extends Shape
{
    public function setWidth(int $width): void
    {
        $this->width = $width;
    }

    public function setHeight(int $height): void
    {
        $this->height = $height;
    }

    public function getArea(): int
    {
        return $this->width * $this->height;
    }
}

class Square extends Shape
{
    private $length = 0;

    public function setLength(int $length): void
    {
        $this->length = $length;
    }

    public function getArea(): int
    {
        return pow($this->length, 2);
    }
}

/**
 * @param Rectangle[] $rectangles
 */
function renderLargeRectangles(array $rectangles): void
{
    foreach ($rectangles as $rectangle) {
        if ($rectangle instanceof Square) {
            $rectangle->setLength(5);
        } elseif ($rectangle instanceof Rectangle) {
            $rectangle->setWidth(4);
            $rectangle->setHeight(5);
        }

        $area = $rectangle->getArea(); 
        $rectangle->render($area);
    }
}

$shapes = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($shapes);

Volver

Principio de segregación de la interfaz

Este principio establece que "Los clientes no deben forzar la dependencia sobre interfaces que no utilizan".

Un buen ejemplo a considerar para demostrar este principio son las clases que requieren objetos de configuración grandes. No requerir clientes con una gran cantidad de opciones de configuración es beneficioso, porque la mayoría del tiempo no necesitará todas las configuraciones. Hacerlas opcional ayuda a prevenir una "interfaz pesada".

Mal:

interface Employee
{
    public function work(): void;

    public function eat(): void;
}

class Human implements Employee
{
    public function work(): void
    {
        // ....trabajando
    }

    public function eat(): void
    {
        // ...... comiendo en la hora de almuerzo
    }
}

class Robot implements Employee
{
    public function work(): void
    {
        //.... trabajando mucho más
    }

    public function eat(): void
    {
        //.... los robots no pueden comer, pero debe implementarse este método
    }
}

Bien:

No todos los trabajadores son empleados, pero cada empleado es un trabajador.

interface Workable
{
    public function work(): void;
}

interface Feedable
{
    public function eat(): void;
}

interface Employee extends Feedable, Workable
{
}

class Human implements Employee
{
    public function work(): void
    {
        // ....trabajando
    }

    public function eat(): void
    {
        //.... comiendo en la hora de almuerzo
    }
}

// Los robots solo pueden trabajar
class Robot implements Workable
{
    public function work(): void
    {
        // ....trabajando
    }
}

Volver

Principio de la inversión de dependencia

Este principio establece dos cosas esenciales:

  1. Los módulos de alto nivel no deben depender de los de bajo nivel. Ambos deben depender de abstracciones.
  2. Las abstracciones no deben depender de detalles. Los detalles deben depender de abstracciones.

Esto puede ser difícil de entender al comienzo, pero si has trabajado con frameworks de PHP (como Symfony), habrás visto alguna implementación de este principio en forma de Inyección de Dependencia (DI). Cuando no hay conceptos idénticos, este principio mantiene en conocimiento a los módulos de alto nivel sobre los módulos de bajo nivel y los configura. Esto se logra mediante la inyección de dependencia. Un gran beneficio de esto es la reducción del acoplamiento entre módulos. El acoplamiento es un patrón de desarrollo muy malo porque hace que el código sea difícil de refactorizar.

Mal:

class Employee
{
    public function work(): void
    {
        // ....trabajando
    }
}

class Robot extends Employee
{
    public function work(): void
    {
        //.... trabajando mucho más
    }
}

class Manager
{
    private $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function manage(): void
    {
        $this->employee->work();
    }
}

Bien:

interface Employee
{
    public function work(): void;
}

class Human implements Employee
{
    public function work(): void
    {
        // ....trabajando
    }
}

class Robot implements Employee
{
    public function work(): void
    {
        //.... trabajando mucho más
    }
}

class Manager
{
    private $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function manage(): void
    {
        $this->employee->work();
    }
}

Volver

No te repitas

Intenta observar el principio no te repitas.

Haz tu mejor esfuerzo en evitar código duplicado. Duplicar código está mal porque significa que hay más de un lugar para modificar algo si necesitas cambiar alguna lógica.

Imagina si tienes un restaurant y mantienes seguimiento de tu inventario: todos tus tomates, cebollas, ajos, especias, etc. Si tienes múltiples listas que mantienen esto, entonces tienes que actualizarlas cuando sirves un plato con tomates en él. Si solo tienes una lista, ¡Hay un solo lugar que actualizar!

A menudo tienes código duplicado porque tienes dos o más cosas ligeramente diferentes, que comparten mucho en común, pero sus diferencias te fuerzan a tener dos o más funciones separadas haciendo mucho de lo mismo. Remover código duplicado significa crear una abstracción que puedan manejar diferentes conjuntos de cosas en una función/modulo/clase.

Lograr una correcta abstracción es crítico, esto porque deberás seguir los principios SOLID dispuestos en la sección clases. Malas abstracciones pueden ser peor que el código duplicado, ¡así que ten cuidado! Dicho esto, si puedes hacer buenas abstracciones, ¡Hazlo! No te repitas, de otra manera te encontrarás actualizando muchos lugares cuando necesites cambiar una cosa.

Mal:

function showDeveloperList(array $developers): void
{
    foreach ($developers as $developer) {
        $expectedSalary = $developer->calculateExpectedSalary();
        $experience = $developer->getExperience();
        $githubLink = $developer->getGithubLink();
        $data = [
            $expectedSalary,
            $experience,
            $githubLink
        ];

        render($data);
    }
}

function showManagerList(array $managers): void
{
    foreach ($managers as $manager) {
        $expectedSalary = $manager->calculateExpectedSalary();
        $experience = $manager->getExperience();
        $githubLink = $manager->getGithubLink();
        $data = [
            $expectedSalary,
            $experience,
            $githubLink
        ];

        render($data);
    }
}

Bien:

function showList(array $employees): void
{
    foreach ($employees as $employee) {
        $expectedSalary = $employee->calculateExpectedSalary();
        $experience = $employee->getExperience();
        $githubLink = $employee->getGithubLink();
        $data = [
            $expectedSalary,
            $experience,
            $githubLink
        ];

        render($data);
    }
}

Muy bien:

Es mejor usar una versión compacta del código.

function showList(array $employees): void
{
    foreach ($employees as $employee) {
        render([
            $employee->calculateExpectedSalary(),
            $employee->getExperience(),
            $employee->getGithubLink()
        ]);
    }
}

Volver

Traducciones

Disponible en muchos otros idiomas:

Volver

Comments
  • [WIN32SS][FONT] Fix font metrics (retry)

    [WIN32SS][FONT] Fix font metrics (retry)

    Purpose

    This PR aims to fix the font metrics. JIRA issue: CORE-11536 Retrial of #706.

    [V] FIXED: Total Commander 8.52 setup: font displayed too large https://jira.reactos.org/browse/CORE-11620

    [V] FIXED: CORE-14378: Effective File Search 6.8.1 german localization text rendering issues https://jira.reactos.org/browse/CORE-14378

    [V] FIXED: CORE-9767: Font garbage in register splash screen in Foxit Reader 7.1.5 https://jira.reactos.org/browse/CORE-9767

    [V] FIXED: Calipers-1 is not displayed correctly. https://jira.reactos.org/browse/CORE-14302

    [V] FIXED: some msi-Installers draw their dialogs too large (example: Click-N-Type Virtual Keyboard 3.03.0412) https://jira.reactos.org/browse/CORE-13161

    [V] FIXED: Irfanview 4.50 font in zoom combobox displayed too large https://jira.reactos.org/browse/CORE-14396

    [V] FIXED: rufus - The window and controls are displayed larger than necessary https://jira.reactos.org/browse/CORE-14461

    TODOs

    • [x] Fix the problem that the line is too tall.
    • [x] Fix the 1-pixel-lower problem.
    • [ ] Fix the small text in Age of Empires.
    bugfix 
    opened by katahiromz 80
  • [NTOSKRNL][KE][INBV] Modern BSOD

    [NTOSKRNL][KE][INBV] Modern BSOD

    Continuation: https://github.com/reactos/reactos/pull/1444

    Purpose

    Seems like BSOD is the main sight. Make BSOD look like Windows 10.

    The goal is to provide user-friendly interface for users and more useful information to developers.

    bsod 0

    JIRA issue: CORE-15474

    Proposed changes

    • Added :( image, palette: image
    • Removed a lot of texts like PSS_MESSAGE_INTRO, BUGCHECK_TECH_INFO
    • BUGCHECK_MESSAGE_INTRO is like in Windows 10 now: Your PC ran into a problem and needs to restart.
    • BUGCODE_ID_DRIVER -> What failed:
    • Added backtrace information: ThFabba@6a9f172
    • Added ErrorScreenStyle to change style of BSOD (0 - modern + QR, 1 - modern, 2 - old + additional info)
    • AdditionalInformation string will contain text for QR-code.
    • You can see "Stop code:" on the screenshots, it's MessageId

    TODO

    • [ ] Implement settings for ErrorScreenStyle.
    • [ ] ~Make QR-codes (is it even possible? :thinking:)~ Fix QR-codes, they are not working anymore.
    • [ ] Rebuild bugcodes.mc to show less text and more useful information. I need your help here, because I have bad English knowledge and I can't rebuild all strings by myself.
    • [x] Make everything better!
    • [ ] Make background color changeable (by creating gradient in palette?)
    • [ ] Remove my comments from code when everything is done (i can't remember functions names and arguments :disappointed:)
    • I've not seen all types of BSOD, so it may fail with some large texts. Please, send some screenshots if you found something.

    Let's make it together

    I need your help to make it better. Please, send your edits/suggestions. For example, new strings for bugcodes.mc. You can help with registry settings and QR code implementation.

    Here is bootcd.iso for tests:

    It's on MEGA.NZ. If the file is not found, maybe I'm updating it right now. To call BSOD, double press LCtrl+Scroll Lock (works even in text-mode setup)

    Additional screenshots

    all bsod 1 bsod 2

    enhancement modding 
    opened by feel-the-dz3n 69
  • IopValidateID()

    IopValidateID()

    [NTOSKRNL] Adding IopValidateID() to test characters in IDs (IRP_MN_QUERY_ID).

    IRP_MN_QUERY_ID is documented here https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-mn-query-id

    opened by vgalnt 41
  • Drop Visual Studio 2010-2013

    Drop Visual Studio 2010-2013

    This year we celebrate a 10th anniversary of Visual Studio 2010 IDE. Good time make this pull request :D

    enhancement 
    opened by Extravert-ir 37
  • [FONT] Improve Marlett font file

    [FONT] Improve Marlett font file

    Purpose

    This PR will improve the Marlett font. Marlett is a Windows/ReactOS font that is used to render GUI components. JIRA issue: CORE-14872

    Proposed change

    • Improve the vector glyphs of the Marlett font file.
    • Append the bitmap glyphs.
    • Update the copyright ane version texts.
    opened by katahiromz 35
  • [NTOS:INBV] Implement rotation bar for boot screen

    [NTOS:INBV] Implement rotation bar for boot screen

    Purpose

    Implement a rotation bar for bootup screen! :smiley:

    Inspired by JIRA issue: CORE-14101

    Fixes issue: CORE-10327

    Proposed changes

    It adds implementation of bootup rotation bar, like in Windows XP.

    Also implemented a rotation line animation which would be showed along with classic progress bar.

    TODO

    Any help is appreciated.

    • [x] Figure out reason of BSOD 0x0000007E and fix it
    • [x] Create system thread for rotation drawing code
    • [x] Figure out reason of another BSOD 0x0000007E and fix it
    • [x] Animation starts with a small delay, improve it
    • [x] Implement a new animation to save original progress bar
    • [x] Reduce patch diff for better compatibility
    enhancement 
    opened by binarymaster 34
  • [THEMES] New theme - Breeze

    [THEMES] New theme - Breeze

    I made a new "Breeze" theme. The design is made in the style of KDE 5. The design is made in the style of KDE 5. VirtualBox_Windows_05_03_2020_11_22_23 VirtualBox_Windows_05_03_2020_11_23_08 VirtualBox_Windows_05_03_2020_11_23_58

    modding 
    opened by EnterWINE 31
  • Enable DXTn by default, it is no longer  patent-protected

    Enable DXTn by default, it is no longer patent-protected

    Enable DXTn in ReactOS by default, as it is no longer protected by the patent.

    see https://jira.reactos.org/browse/CORE-13455 for the details.

    help wanted 
    opened by jeditobe 30
  • [EXPLORER][SHELL32] Implement 'Show the Desktop' action of Task Bar

    [EXPLORER][SHELL32] Implement 'Show the Desktop' action of Task Bar

    Purpose

    This PR will implement "Show the Desktop" action of Task Bar, and enable Win+D and Win+M key.

    JIRA issue: CORE-14318, CORE-14806

    Proposed changes

    • Implement IShellDispatch4::ToggleDesktop.
    • Implement some commands in CTrayWindow.
    • Add "sdk/include/reactos/traycmd.h" for tray commands.
    • Fix task window switching.
    • Improve SwitchToThisWindow function and use it.
    opened by katahiromz 28
  • [AC97] Improve version info

    [AC97] Improve version info

    Proposed changes

    • Improve AC'97 audio driver version info
    enhancement drivers 
    opened by TAN-Gaming 0
  • [AC97] Enable on all Intel AC'97 audio controllers

    [AC97] Enable on all Intel AC'97 audio controllers

    This driver might work with all Intel AC'97 audio controllers. So far I tested it successfully on 82801FBM (ICH6-M). IMG_20220216_1735182

    enhancement drivers 
    opened by archeYR 0
  • [SHELL32] CDefaultContextMenu: Provide a fallback property sheet

    [SHELL32] CDefaultContextMenu: Provide a fallback property sheet

    opened by learn-more 3
  • [NTOSKRNL] Implement JobObjectBasicProcessIdList

    [NTOSKRNL] Implement JobObjectBasicProcessIdList

    null

    enhancement kernel&hal 
    opened by learn-more 0
  • [FREELDR:PC98] Don't do the HighRes check everytime a character is written with writechr.

    [FREELDR:PC98] Don't do the HighRes check everytime a character is written with writechr.

    Purpose

    See this PR's title. I suppose that this setting won't change between calls (when e.g. done from the other writestr helpers).

    Proposed changes

    Do the HighRes check and cache the computed value of VramSegment only once, then reuse that latter one.

    TODO

    • [ ] Retest again once other blocker PR #4358 gets fixed.
    enhancement freeldr 
    opened by HBelusca 1
  • [FREELDR] Don't load a full FAT12/16 table when loading FREELDR.SYS.

    [FREELDR] Don't load a full FAT12/16 table when loading FREELDR.SYS.

    Purpose

    At least the way we currently do it and have our data layed out in memory. Otherwise we risk corrupting either the FAT-loading code, or the contents of FREELDR.SYS being loaded, when said FAT table is quite large.

    Supersedes PR #2182.

    JIRA issues: CORE-15427, CORE-14558, CORE-16195, CORE-13740

    Proposed changes

    • Adapt the simple "cache" code from FAT32.
    • But read two FAT sectors everytime in order to cover the case where one FAT12 entry data crosses sector boundary (since FAT12 entry data is 12 bytes, and therefore NOT byte / 2-byte aligned).

    TODO

    • [ ] Fully test this under Bochs+IDA Pro.
    • [ ] Test the FAT16 modifications.
    bugfix freeldr 
    opened by HBelusca 1
  • [NTUSER][USER32] Create default IME window!!!

    [NTUSER][USER32] Create default IME window!!!

    Purpose

    The IME window is an invisible control center that manages IME-related messaging. The default IME window is created for each top-level window in some condition.

    JIRA issue: CORE-11700

    Proposed changes

    • Add IntNeedDefaultImeWindow, IntCanDestroyDefaultImeWindow, and co_IntCreateDefaultImeWindow helper functions.
    • Create the default IME window in IntCreateWindow function in some condition.
    • Destroy the default IME window in co_UserDestroyWindow in some condition.
    • Modify NtUserSetImeOwnerWindow function.

    TODO

    • [ ] Do tests.
    enhancement 
    opened by katahiromz 9
  • [TRANSLATION][SYSSETUP] Croatian translation and hotkey fix

    [TRANSLATION][SYSSETUP] Croatian translation and hotkey fix

    Purpose

    Translate 2nd stage of setup to Croatian and update comctl to fix hotkey

    JIRA issue: N/A

    Proposed changes

    Update few .rc files

    And yes, I watched on newlines.

    TODO

    • [ ] Fix hotkey and translation on buttons (comctl) - I don't know why don't work
    TRANSLATION 
    opened by Andrej123456789 0
  • [B57XX] Broadcom NetXtreme Driver Bringup

    [B57XX] Broadcom NetXtreme Driver Bringup

    Purpose

    The b57xx is a NDIS miniport device driver for Broadcom NetXtreme/NetLink ethernet adapters. This will allow machines with select broadcom 57xx NICs to connect to ethernet out of the box.

    TODO

    • [ ] Advanced feature configuration. Users may find it useful to configure advanced parameters for their adapter in device manager. Some parameters may be: The use of jumbo rings, Ring sizes, flow control.
    • [ ] Add more supported adapters. The ones in this PR are highly likely to work as-is (and if they don't, ReactOS should continue as normal). Additional broadcom NetXtreme/NetLink adapters may need additional special cases in the b57xx driver. (NOTE: If you are adding a supported device, make sure to also add it to the hardware list in hardware.c)
    • [ ] 5700 NICs are not fully configured in the device driver. These adapters use external SSRAM and are the only ones to do so. As far as I can tell, these were prototype devices and are incredibly rare. If you have one, please let me know.
    • [ ] Receive & Send VLAN. VLAN tags are automatically stripped and stored in the VLANTag field in the receive or send buffer descriptor respectively. The b57xx driver should handle this appropriately.
    • [ ] Windows Server 2003 compatibility. Currently the b57xx driver is not compatible with Windows Server 2003. Thus it could not be ran through the verifier.

    Receive

    • [ ] Checksum verification. Currently, all frames that the MAC accepts are also accepted by the b57xx driver and passed to NDIS. The MAC automatically calculates the correct pseudo-header checksums for TCP/UDP segments and IP packets. The host driver could check receive return buffer descriptors for errors and handle them appropriately.
    • [ ] Many, but not all 57xx devices support Jumbo receive rings. Adding support for Jumbo receive rings for supported devices may bring performance enhancements.

    Transmit

    • [ ] Network layer & Transport layer fragmentation. If IP fragments and/or TCP/UDP fragments are being transmitted, the driver should set the appropriate flags in send buffer descriptor.
    • [ ] Transmit TCP/UDP Pseudo-header Checksums. This could be offloaded to hardware but Broadcom recommends this be done in software as some adapters calculate this incorrectly in hardware. It is also possible for the host driver to retrieve broadcom's official TSO firmware and have this done correctly in hardware.

    Verified Devices

    | Device | State | Tester | | - | - | - | | BCM5700 | ❓ | | | BCM5701 | ❓ | | | BCM5702 | ❓ | | | BCM5703C | ❓ | | | BCM5703S | ❓ | | | BCM5704C | ❓ | | | BCM5704S | ❓ | | | BCM5705 | ❓ | | | BCM5705M | ❓ | | | BCM5788 | ❓ | | | BCM5721 | ✔️ | @DarkFire01 | | BCM5751 | ❓ | | | BCM5751M | ✔️ | @archeYR | | BCM5752 | ✔️ | @nothotscott | | BCM5752M | ❓ | | | BCM5714C | ❓ | | | BCM5714S | ❓ | | | BCM5715C | ❓ | | | BCM5715S | ❓ | | | BCM5722 | ❓ | | | BCM5755 | ❓ | | | BCM5755M | ❓ | | | BCM5754 | ❓ | | | BCM5754M | ❓ | | | BCM5756M | ❓ | | | BCM5757 | ❓ | | | BCM5786 | ❓ | | | BCM5787 | ❓ | | | BCM5787M | ❓ | | | BCM5784M | ✔️ | @archeYR | | BCM5764M | ❓ | |

    enhancement drivers 
    opened by nothotscott 0
  • [TRANSLATION] Update Hong Kong Chinese (zh-HK) translation - Part 2

    [TRANSLATION] Update Hong Kong Chinese (zh-HK) translation - Part 2

    Purpose

    Add new language support for zh-HK.

    JIRA issue: None

    Proposed changes

    • Added zh-HK translation for following files:
      • [BASE/SERVICES/W32TIME]
      • [BASE/SYSTEM/...] (except CMD console only applications)
      • [FDEBUG]
      • [DLL/CPL/...] (except Wine related applications)
      • [DLL/SHELLEXT/...]
      • [DLL/WIN32/...] (not all applications are translated, and Wine related applications are excluded from this part)
      • [MODULES/ROSAPPS/APPLICATIONS/...] (not all applications are translated)
      • [SCREENSAVERS]
      • [NTVDM]
      • [USERSRV]
    • Translation Improvement
    • Fix header for zh-TW and zh-HK translation files

    TODO

    This PR is a series of 4 seperate PRs, I will add translation according to following sequences:

    1. Base (except CMD, network and Wine related applicatons), SYSSETUP, Media Done in PR 3941
    2. CPL applications, shellext, DLL, Modules <-THIS PR
    3. CMD console applicatons
    4. Wine related applicatons

    Each phase will have its own PR so no need to wait until everything is done. I think it can reduce the workload of reviewers for limiting the files needs for translation for each phase.

    CC: @reactos/lang-chinese

    TRANSLATION freeldr 
    opened by eason329 0
Owner
Francisco Borquez
Francisco Borquez
Jéssica Paula 7 Aug 12, 2022
Clean Code concepts adapted for PHP - A guide for producing readable, reusable, and refactorable PHP software

Clean Code concepts adapted for PHP - A guide for producing readable, reusable, and refactorable PHP software

Fabio Soares 172 Dec 25, 2022
Clean Code concepts adapted for PHP

Clean Code PHP Table of Contents Introduction Variables Use meaningful and pronounceable variable names Use the same vocabulary for the same type of v

Piotr Plenik 11.3k Jan 7, 2023
Clean Code concepts adapted for PHP

PHP Temiz Kod İçindekiler Giriş Değişkenler Anlamlı ve telaffuz edilebilir değişken isimleri kullanın Aynı türden değişkenler için aynı kelimeleri kul

Anıl Özmen 122 Dec 25, 2022
Este es un sitema bibliotecario para registro de adquisiciones y prestamos para INATEC Siuna

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

James Reyes 3 Mar 26, 2022
Plugin de Wordpress para criar um Hacker News-like para o ManualdoUsuario.net

?? Órbita Plugin de Wordpress para criar um painel de debates baseado em links, similar ao Hacker News, para o Manual do Usuário. Rodar o projeto Requ

Gabriel Nunes 7 Nov 14, 2022
PHP Simple M3U Parser, it clean the playlist and remove duplicate

SimpleM3UParser PHP Simple M3U Playlist Parser, it clean, remove duplicate, and group the playlist. Usage see example.php <?php require_once "M3UPars

erwin solihin 3 May 30, 2022
A super simple, clean and pretty error handler that replace the default error handler of PHP. You need only include this file!

php-custom-error-handler A super simple, clean and pretty error handler that replace the default error handler of PHP. You need just include only this

null 6 Nov 7, 2022
my personal example of Laravel clean architecture

what is this repo about Clean Architect Laravel ###run we assume docker desktop is up and running open up a terminal cd project directory run "cp .env

Sadegh Salari 37 Dec 23, 2022
A PocketMine-MP plugin that can reduce or clean up the chunks in your world

AutoClearChunk A PocketMine-MP plugin that can reduce or clean up the chunks in your world Features Custom Clear Message Custom Clear Interval Time Pe

HazardTeam 6 Mar 10, 2022
POC d'un projet Clean Architecture + DDD

Proof Of Concept - Clean Architecture & DDD Installation Dans un premier temps, cloner le repository : git clone https://github.com/TBoileau/rse cd rs

Thomas Boileau 11 Sep 3, 2022
A collection of functions to clean up WordPress

Clean WordPress Admin A collection of functions to clean up WordPress front and back-end to make it easier for editors to work and for you to look at

Vincent Orback 411 Dec 28, 2022
A faster drop in replacement for bin/magento cache:clean with file watcher

"You know, hope is a mistake. If you can't fix what's broken, you'll, uh... you'll go insane." - Max Rockatansky Magento 2 Cache Clean A faster drop i

mage2tv 460 Dec 26, 2022
Dockerise Symfony Application (Symfony 6 + Clean Architecture+ DDD+ CQRS + Docker + Xdebug + PHPUnit + Doctrine ORM + JWT Auth + Static analysis)

Symfony Dockerise Symfony Application Install Docker Install Docker Compose Docker PHP & Nginx Create Symfony Application Debugging Install Xdebug Con

null 48 Jan 5, 2023
Clean Architecture, DDD and CQRS using Symfony 6

Task manager system using Clean Architecture, DDD and CQRS. Environment setup Install Docker Clone the project: git clone https://github.com/k0t9i/Tas

null 3 Sep 5, 2022
Test and enforce architectural rules in your Laravel applications. Keep your app's architecture clean and consistent!

Laravel Arkitect Laravel Arkitect lets you test and enforce your architectural rules in your Laravel applications, and it's a PHPArkitect wrapper for

SMorteza Ebadi 55 Dec 17, 2022
Result of our code-along meetup writing PHP 8.1 code

PHP 8.1 Demo Code This code demonstrates various PHP 8.0 and 8.1 features in a realistic, functional (but incomplete) codebase. The code is part of so

azPHP 2 Nov 14, 2021
Dead Code Detector (DCD) for PHP code.

This project is no longer maintained and its repository is only kept for archival purposes. PHP Dead Code Detector (PHPDCD) phpdcd is a Dead Code Dete

Sebastian Bergmann 406 Dec 30, 2022
⚗️ Adds code analysis to Laravel improving developer productivity and code quality.

⚗️ About Larastan Larastan was created by Can Vural and Nuno Maduro, got artwork designed by @Caneco, is maintained by Can Vural, Nuno Maduro, and Vik

Nuno Maduro 4.4k Jan 4, 2023