Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Defensive Programming

Defensive Programming

Defensive programming may sound like something your granddad did after the war, but it's key to reducing the number of bugs and increasing maintainability. We're going to look at what defensive programming is and some steps to doing it in PHP.

Christopher Pitt

June 26, 2015
Tweet

More Decks by Christopher Pitt

Other Decks in Programming

Transcript

  1. /** * @entity * @table(name="cakes") */ class Cake { /**

    * @var string * @column(type="string") */ protected $type; /** * @param string $type */ public function setType($type) { $this->type = $type; } }
  2. $config = Setup::createAnnotationMetadataConfiguration( ["src"], $isDevMode = true ); $cake =

    new Cake(); $cake->setType("Brownie"); $manager = EntityManager::create($connection, $config); $manager->persist($cake); $manager->flush(); # github.com/doctrine/doctrine2
  3. $query = $factory->newSelect(); $query ->cols(["id", "type"]) ->from("cakes") ->where("cake.type = ?",

    "brownie"); print $query->getStatement(); # github.com/auraphp/aura.sqlquery
  4. $before = "This is an example of<br> some html content!

    <a href='http://twitter.com'>click for drama</a> <script>console.log('boo!');</script>"; $after = strip_tags($before, "<br>"); "This is an example of<br> some html content!" # php.net/function.strip_tags
  5. $before = "This is an example of<br> some html content!

    <a href='http://twitter.com'>click for drama</a> <script>console.log('boo!');</script>"; $after = htmlentities($before); "This is an example of&lt;br&gt; some html content! &lt;a href='http://twitter.com'&gt;click for drama&lt;/a&gt; &lt;script&gt;console.log('boo!');&lt;/script&gt;" # php.net/function.htmlentities
  6. function addTaskToQueue(Task $task, SplQueue $queue) { $clone = clone $queue;

    $clone->enqueue($task); return $clone; } function fetchProducts(array $products, Closure $then) { $then( array_map(function(Product $product) { return file_get_contents("http://api.com/{$product->id}"); }, $products) ); }
  7. function addTaxToPrice(float $percentage, float $price): float { return $price +

    ($price * $percentage); } declare(strict_types=1); $newPrice = addTaxToPrice(0.14, 120.0); # wiki.php.net/rfc/return_types # wiki.php.net/rfc/scalar_type_hints_v5
  8. /** * @param float $percentage * @param float $price *

    @return float */ function addTaxToPrice($percentage, $price) { assert(is_float($percentage)); assert(is_float($price)); return $price + ($price * $percentage); } # php.net/function.assert
  9. /** * @param float $amount * @param string $reason *

    @param DateTime $timestamp */ public function __construct($amount, $reason, DateTime $timestamp) { assert(is_float($amount)); assert(is_string($reason)); $this->amount = $amount; $this->reason = $reason; $this->timestamp = $timestamp; }
  10. $timestamp = new DateTime("2015-06-26 12:15:00"); $transaction1 = new Transaction( 12.0,

    "bought a burrito", $timestamp ); $transaction2 = new Transaction( 9.0, "had shoes polished", $timestamp ); $timestamp->setTime(12, 20);
  11. /** * @param string $type */ public function withType($type) {

    assert(is_string($type)); $clone = clone $this; $clone->type = $type; return $clone; }
  12. /** * @param string $property * @param mixed $value */

    public function cloneWith($property, $value) { $clone = clone $this; $clone->$property = $value; return $clone; } /** * @param string $type */ public function withType($type) { assert(is_string($type)); return $this->cloneWith("type", $type); }
  13. function sendOrdersAndRenderInvoice(array $orders, $templatePath) { foreach ($orders as $order) {

    if (!($order instanceof Order)) { throw new InvalidArgumentException("Invalid order type"); } try { $this->api->send($order); } catch (ApiException $exception) { $this->logger->log("There was a problem sending an order"); throw $exception; } } if (!file_exists($templatePath)) { throw new InvalidArgumentException("Template not found"); } $template = file_get_contents($templatePath); return $this->renderer->render($template, $orders); }
  14. function sendOrders(array $orders) { $this->validateOrders($orders); foreach ($orders as $order) {

    $this->sendOrder($order); } } function validateOrders(array $orders) { foreach ($orders as $order) { if (!($order instanceof Order)) { throw new InvalidArgumentException("Invalid order type"); } }; } function sendOrder(Order $order) { try { $this->api->send($order); } catch (ApiException $exception) { $this->logger->log("There was a problem sending an order"); throw $exception; } }
  15. function openAccount(AccountHolder $holder, AccountType $type) { if ($holder->getAge() > 18

    || $holder->hasParentPermission()) { if (in_array($type->getName(), $holder->getAllowedAccountTypes())) { if ($this->canOpenAccountType($type->getName())) { $this->openApprovedAccount($holder, $type); return true; } } } return false; }
  16. function openAccount(AccountHolder $holder, AccountType $type) { if ($holder->getAge() < 18

    && !$holder->hasParentPermission()) { return false; } if (!in_array($type->getName(), $holder->getAllowedAccountTypes())) { return false; } if ($this->canOpenAccountType($type->getName())) { return false; } $this->openApprovedAccount($holder, $type); return true; }