Skip to content

Instantly share code, notes, and snippets.

@zonuexe
Last active September 5, 2017 10:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zonuexe/c9a2da09b66048c8bb221631f79bace5 to your computer and use it in GitHub Desktop.
Save zonuexe/c9a2da09b66048c8bb221631f79bace5 to your computer and use it in GitHub Desktop.
PHP: Clean Code (clean-code-php) 蜜柑薬 ref: http://qiita.com/tadsan/items/c47eb327684530721e8a
The MIT License (MIT)
Copyright (c) 2016 Ryan McDermott
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
$ymdstr = $moment->format('y-m-d');
$l = ['Austin', 'New York', 'San Francisco'];
for ($i = 0; $i < count($l); $i++) {
$li = $l[$i];
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// また `$li` が出てきたけど… これは何?
dispatch($li);
}
$locations = ['Austin', 'New York', 'San Francisco'];
foreach ($locations as $location) {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch($location);
});
class Car
{
public $carMake;
public $carModel;
public $carColor;
//...
}
class Car
{
public $make;
public $model;
public $color;
//...
}
function createMicrobrewery($name = null) {
$breweryName = $name ?: 'Hipster Brew Co.';
// ...
}
function createMicrobrewery($breweryName = 'Hipster Brew Co.') {
// ...
}
function createMenu($title, $body, $buttonText, $cancellable) {
// ...
}
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) {
// ...
}
function emailClients($clients) {
foreach ($clients as $client) {
$clientRecord = $db->find($client);
if ($clientRecord->isActive()) {
email($client);
}
}
}
function emailClients($clients) {
$activeClients = activeClients($clients);
array_walk($activeClients, 'email');
}
function activeClients($clients) {
return array_filter($clients, 'isClientActive');
}
function isClientActive($client) {
$clientRecord = $db->find($client);
return $clientRecord->isActive();
}
$currentDate = $moment->format('y-m-d');
class Email
{
//...
public function handle()
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
// メッセージのハンドルって、これは何やってるの…? ファイルに書き込むの?
$message->handle();
class Email
{
//...
public function send()
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
// 送信する。一目瞭然。
$message->send();
function parseBetterJSAlternative($code)
{
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach ($regexes as $regex) {
foreach ($statements as $statement) {
// ...
}
}
$ast = [];
foreach ($tokens as $token) {
// 字句解析...
}
foreach ($ast as $node) {
// 構文解析...
}
}
function tokenize($code)
{
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach ($regexes as $regex) {
foreach ($statements as $statement) {
$tokens[] = /* ... */;
}
}
return $tokens;
}
function lexer($tokens)
{
$ast = [];
foreach ($tokens as $token) {
$ast[] = /* ... */;
}
return $ast;
}
function parseBetterJSAlternative($code)
{
$tokens = tokenize($code);
$ast = lexer($tokens);
foreach ($ast as $node) {
// parse...
}
}
class Tokenizer
{
public function tokenize($code)
{
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach ($regexes as $regex) {
foreach ($statements as $statement) {
$tokens[] = /* ... */;
}
}
return $tokens;
}
}
class Lexer
{
public function lexify($tokens)
{
$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($code)
{
$tokens = $this->tokenizer->tokenize($code);
$ast = $this->lexer->lexify($tokens);
foreach ($ast as $node) {
// parse...
}
}
}
function createFile($name, $temp = false) {
if ($temp) {
touch('./temp/'.$name);
} else {
touch($name);
}
}
function createFile($name) {
touch($name);
}
function createTempFile($name) {
touch('./temp/'.$name);
}
// グローバル変数がこれらの関数から参照される
// もし同じ変数名を使用しようとする別の関数があったら、この処理は壊れます
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
global $name;
$name = preg_split('/ /', $name);
}
splitIntoFirstAndLastName();
var_dump($name); // ['Ryan', 'McDermott'];
getUserInfo();
getClientData();
getCustomerRecord();
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName($name) {
return preg_split('/ /', $name);
}
$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);
var_dump($name); // 'Ryan McDermott';
var_dump($newName); // ['Ryan', 'McDermott'];
function config()
{
return [
'foo' => 'bar',
]
}
// config.php
return [
'foo' => 'bar',
];
class Configuration
{
private $configuration = [];
public function __construct(array $configuration)
{
$this->configuration = $configuration;
}
public function get($key)
{
return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
}
}
$configuration = new Configuration($configuration);
class DBConnection
{
private static $instance;
private function __construct($dsn)
{
// ...
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// ...
}
$singleton = DBConnection::getInstance();
class DBConnection
{
public function __construct(array $dsn)
{
// ...
}
// ...
}
$connection = new DBConnection($dsn);
if ($article->state === 'published') {
// ...
}
if ($article->isPublished()) {
// ...
}
function isDOMNodeNotPresent($node) {
// ...
}
if (!isDOMNodeNotPresent($node)) {
// ...
}
function isDOMNodePresent($node) {
// ...
}
if (isDOMNodePresent($node)) {
// ...
}
class Airplane {
// ...
public function getCruisingAltitude() {
switch ($this->type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
}
class Airplane {
// ...
}
class Boeing777 extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getPassengerCount();
}
}
class AirForceOne extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude();
}
}
class Cessna extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
function travelToTexas($vehicle)
{
if ($vehicle instanceof Bicycle) {
$vehicle->peddleTo(new Location('texas'));
} elseif ($vehicle instanceof Car) {
$vehicle->driveTo(new Location('texas'));
}
}
function travelToTexas(Traveler $vehicle)
{
$vehicle->travelTo(new Location('texas'));
}
function combine($val1, $val2)
{
if (!is_numeric($val1) || !is_numeric($val2)) {
throw new \Exception('Must be of type Number');
}
return $val1 + $val2;
}
function combine(int $val1, int $val2)
{
return $val1 + $val2;
}
function oldRequestModule($url) {
// ...
}
function newRequestModule($url) {
// ...
}
$req = new newRequestModule($requestUrl);
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
function requestModule($url) {
// ...
}
$req = new requestModule($requestUrl);
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
// 86400って何…??
addExpireAt(86400);
class BankAccount {
public $balance = 1000;
}
$bankAccount = new BankAccount();
// Buy shoes...
$bankAccount->balance -= 100;
class BankAccount {
private $balance;
public function __construct($balance = 1000) {
$this->balance = $balance;
}
public function withdrawBalance($amount) {
if ($amount > $this->balance) {
throw new \Exception('Amount greater than available balance.');
}
$this->balance -= $amount;
}
public function depositBalance($amount) {
$this->balance += $amount;
}
public function getBalance() {
return $this->balance;
}
}
$bankAccount = new BankAccount();
// Buy shoes...
$bankAccount->withdrawBalance($shoesPrice);
// Get balance
$balance = $bankAccount->getBalance();
public function __construct($balance = 1000) {
$this->balance = $balance;
}
public function withdrawBalance($amount) {
if ($amount > $this->balance) {
throw new \Exception('Amount greater than available balance.');
}
$this->balance -= $amount;
}
public function depositBalance($amount) {
$this->balance += $amount;
}
public function getBalance() {
return $this->balance;
}
class Employee {
public $name;
public function __construct($name) {
$this->name = $name;
}
}
$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->name; // Employee name: John Doe
### Make objects have private/protected members
:no_good_tone1: **Bad** (だめ)
```php
class Employee {
public $name;
public function __construct($name) {
$this->name = $name;
}
}
$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->name; // Employee name: John Doe
class Employee {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->getName(); // Employee name: John Doe
class UserSettings {
private $user;
public function __construct($user) {
$this->user = user;
}
public function changeSettings($settings) {
if ($this->verifyCredentials()) {
// ...
}
}
private function verifyCredentials() {
// ...
}
}
public function changeSettings($settings) {
if ($this->verifyCredentials()) {
// ...
}
}
private function verifyCredentials() {
// ...
}
class UserAuth {
private $user;
public function __construct($user) {
$this->user = user;
}
public function verifyCredentials() {
// ...
}
}
class UserSettings {
private $user;
public function __construct($user) {
$this->user = $user;
$this->auth = new UserAuth($user);
}
public function changeSettings($settings) {
if ($this->auth->verifyCredentials()) {
// ...
}
}
}
abstract class Adapter
{
protected $name;
public function getName()
{
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)
{
$this->adapter = $adapter;
}
public function fetch($url)
{
$adapterName = $this->adapter->getName();
if ($adapterName === 'ajaxAdapter') {
return $this->makeAjaxCall($url);
} elseif ($adapterName === 'httpNodeAdapter') {
return $this->makeHttpCall($url);
}
}
protected function makeAjaxCall($url)
{
// request and return promise
}
protected function makeHttpCall($url)
{
// request and return promise
}
}
interface Adapter
{
public function request($url);
}
class AjaxAdapter implements Adapter
{
public function request($url)
{
// request and return promise
}
}
class NodeAdapter implements Adapter
{
public function request($url)
{
// request and return promise
}
}
class HttpRequester
{
private $adapter;
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
}
public function fetch($url)
{
return $this->adapter->request($url);
}
}
class Rectangle {
private $width, $height;
public function __construct() {
$this->width = 0;
$this->height = 0;
}
public function setColor($color) {
// ...
}
public function render($area) {
// ...
}
public function setWidth($width) {
$this->width = $width;
}
public function setHeight($height) {
$this->height = $height;
}
public function getArea() {
return $this->width * $this->height;
}
}
class Square extends Rectangle {
public function setWidth($width) {
$this->width = $this->height = $width;
}
public function setHeight(height) {
$this->width = $this->height = $height;
}
}
function renderLargeRectangles($rectangles) {
foreach($rectangle in $rectangles) {
$rectangle->setWidth(4);
$rectangle->setHeight(5);
$area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20.
$rectangle->render($area);
});
}
$rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($rectangles);
abstract class Shape {
private $width, $height;
abstract public function getArea();
public function setColor($color) {
// ...
}
public function render($area) {
// ...
}
}
class Rectangle extends Shape {
public function __construct {
parent::__construct();
$this->width = 0;
$this->height = 0;
}
public function setWidth($width) {
$this->width = $width;
}
public function setHeight($height) {
$this->height = $height;
}
public function getArea() {
return $this->width * $this->height;
}
}
class Square extends Shape {
public function __construct {
parent::__construct();
$this->length = 0;
}
public function setLength($length) {
$this->length = $length;
}
public function getArea() {
return pow($this->length, 2);
}
}
function renderLargeRectangles($rectangles) {
foreach($rectangle in $rectangles) {
if ($rectangle instanceof Square) {
$rectangle->setLength(5);
} else if ($rectangle instanceof Rectangle) {
$rectangle->setWidth(4);
$rectangle->setHeight(5);
}
$area = $rectangle->getArea();
$rectangle->render($area);
});
}
$shapes = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($shapes);
// constキーワードで、全部大文字の定数名で定義する
interface DateGlobal {
const SECONDS_IN_A_DAY = 86400;
}
addExpireAt(DateGlobal::SECONDS_IN_A_DAY);
interface WorkerInterface {
public function work();
public function eat();
}
class Worker implements WorkerInterface {
public function work() {
// ....working
}
public function eat() {
// ...... eating in launch break
}
}
class SuperWorker implements WorkerInterface {
public function work() {
//.... working much more
}
public function eat() {
//.... eating in launch break
}
}
class Manager {
/** @var WorkerInterface $worker **/
private $worker;
public function setWorker(WorkerInterface $worker) {
$this->worker = $worker;
}
public function manage() {
$this->worker->work();
}
}
interface WorkerInterface extends FeedableInterface, WorkableInterface {
}
interface WorkableInterface {
public function work();
}
interface FeedableInterface {
public function eat();
}
class Worker implements WorkableInterface, FeedableInterface {
public function work() {
// ....working
}
public function eat() {
//.... eating in launch break
}
}
class Robot implements WorkableInterface {
public function work() {
// ....working
}
}
class SuperWorker implements WorkerInterface {
public function work() {
//.... working much more
}
public function eat() {
//.... eating in launch break
}
}
class Manager {
/** @var $worker WorkableInterface **/
private $worker;
public function setWorker(WorkableInterface $w) {
$this->worker = $w;
}
public function manage() {
$this->worker->work();
}
}
class Worker {
public function work() {
// ....working
}
}
class Manager {
/** @var Worker $worker **/
private $worker;
public function __construct(Worker $worker) {
$this->worker = $worker;
}
public function manage() {
$this->worker->work();
}
}
class SuperWorker extends Worker {
public function work() {
//.... working much more
}
}
interface WorkerInterface {
public function work();
}
class Worker implements WorkerInterface {
public function work() {
// ....working
}
}
class SuperWorker implements WorkerInterface {
public function work() {
//.... working much more
}
}
class Manager {
/** @var Worker $worker **/
private $worker;
public function __construct(WorkerInterface $worker) {
$this->worker = $worker;
}
public function manage() {
$this->worker->work();
}
}
class Car {
private $make, $model, $color;
public function __construct() {
$this->make = 'Honda';
$this->model = 'Accord';
$this->color = 'white';
}
public function setMake($make) {
$this->make = $make;
}
public function setModel($model) {
$this->model = $model;
}
public function setColor($color) {
$this->color = $color;
}
public function dump() {
var_dump($this->make, $this->model, $this->color);
}
}
$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();
class Car {
private $make, $model, $color;
public function __construct() {
$this->make = 'Honda';
$this->model = 'Accord';
$this->color = 'white';
}
public function setMake($make) {
$this->make = $make;
// NOTE: Returning this for chaining
return $this;
}
public function setModel($model) {
$this->model = $model;
// NOTE: Returning this for chaining
return $this;
}
public function setColor($color) {
$this->color = $color;
// NOTE: Returning this for chaining
return $this;
}
public function dump() {
var_dump($this->make, $this->model, $this->color);
}
}
$car = (new Car())
->setColor('pink')
->setMake('Ford')
->setModel('F-150')
->dump();
class Employee {
private $name, $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
// ...
}
// Bad because Employees "have" tax data.
// EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
private $ssn, $salary;
public function __construct($name, $email, $ssn, $salary) {
parent::__construct($name, $email);
$this->ssn = $ssn;
$this->salary = $salary;
}
// ...
}
class EmployeeTaxData {
private $ssn, $salary;
public function __construct($ssn, $salary) {
$this->ssn = $ssn;
$this->salary = $salary;
}
// ...
}
class Employee {
private $name, $email, $taxData;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
public function setTaxData($ssn, $salary) {
$this->taxData = new EmployeeTaxData($ssn, $salary);
}
// ...
}
function showDeveloperList($developers)
{
foreach ($developers as $developer) {
$expectedSalary = $developer->calculateExpectedSalary();
$experience = $developer->getExperience();
$githubLink = $developer->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
function showManagerList($managers)
{
foreach ($managers as $manager) {
$expectedSalary = $manager->calculateExpectedSalary();
$experience = $manager->getExperience();
$githubLink = $manager->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
function showList($employees)
{
foreach ($employees as $employee) {
$expectedSalary = $employee->calculateExpectedSalary();
$experience = $employee->getExperience();
$githubLink = $employee->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches[1], $matches[2]);
function showList($employees)
{
foreach ($employees as $employee) {
render([
$employee->calculateExpectedSalary(),
$employee->getExperience(),
$employee->getGithubLink()
]);
}
}
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
list(, $city, $zipCode) = $matchers;
saveCityZipCode($city, $zipCode);
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?<city>.+?)\s*(?<zipCode>\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches['city'], $matches['zipCode']);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment