SlideShare ist ein Scribd-Unternehmen logo
1 von 143
Downloaden Sie, um offline zu lesen
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Hello, From Austin, Texas, USA
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
https://twitter.com/chrisholland
http://bit.ly/exceptions-ftw
Slides:
Validations
Validations: Painful
Validations: Painful
❖ if (
$this->validateThingOne() &&
$this->validateThingTwo() &&
$this->validateThingThree()
) {
doSomeTransaction();
return true; //signal that the thing was done
} else {
return false; //signal the thing was not done
}
Validations: Painful
❖ private function validateThingOne()
{
if (//someErrorCondition) {
return false;
} else {
return true;
}
}
Validations: Painful
❖ private function validateThingTwo()
{
if (//someErrorCondition) {
return false;
} else {
return true;
}
}
Validations: Painful
❖ private function validateThingThree()
{
if (//someErrorCondition) {
return false;
} else {
return true;
}
}
Validations: Painful
❖ Validation methods must “return true / false” …
❖ to signal success or failure
❖ upstream methods must adapt behavior …
❖ based on true / false
❖ Code has more nesting
Nesting?
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Validations: Graceful
Validations: Graceful
❖ $this->validateThingOne();
$this->validateThingTwo();
$this->validateThingThree();
//if any exception thrown above
//doSomeTransaction() will not run
doSomeTransaction();
Validations: Graceful
❖ private function validateThingOne()
{
if (//someErrorCondition) {
throw new InvalidThingOneException();
}
}
Validations: Graceful
❖ private function validateThingTwo()
{
if (//someErrorCondition) {
throw new InvalidThingTwoException();
}
}
Validations: Graceful
❖ private function validateThingThree()
{
if (//someErrorCondition) {
throw new InvalidThingThreeException();
}
}
Validations: Graceful
❖ Validation methods don’t need to return anything
❖ they only need to “throw Exception” on failure
❖ upstream methods don’t have to care
❖ Code has far less nesting.
Another Use-Case
Get Products for User
Get Products for User
❖ Failure Modes:
❖ User has no products …
❖ That’s okay
❖ User could not be found?
❖ That’s not normal
Possible Stack
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ ProductRepository::getProducts(User $user) : array
Possible Stack
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ ProductRepository::getProducts(User $user) : array
Possible Stack
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ ProductRepository::getProducts(User $user) : array
UserRepository::
getUserById(int $userId) : User
getUserById(int $userId) : User
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
getUserById(int $userId) : User
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
getUserById(int $userId) : User
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
What if Bogus?
What Happens When …
… User Not Found?
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
getUserById(int $userId) : User
❖ What happens when no user is found?
❖ return null?
❖ return false?
❖ return -1?
UserRepository
UserRepository
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null; //no Result was found, returning null
}
}
ProductsService
ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
//we don’t want to send a ‘null’ $user to getProducts
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService
❖ If no user is found …
❖ I return an empty array … of Products
❖ … because it feels convenient
❖ is this, however, elegant?
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products
❖ no user was found
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products <— That’s OK
❖ no user was found
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products <— That’s OK
❖ no user was found
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products <— That’s OK
❖ no user was found <— strange behavior
ProductsService: Alternative
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
X
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1;
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1; //using -1 to “signal” no user found
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1; //using -1 to “signal” no user found
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1;
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1;
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
Inconsistent Return Types !
ProductsService: Alternative
❖ I return “-1” to “signal” that no user could be found
❖ so my Service can now either return:
❖ an array of products
❖ or -1
❖ … that’s not … “great”.
ProductsController
ProductsController
❖ Based on what the Service returns, the Controller has two
options:
❖ Option 1: also always return an array
❖ Option 2: handle “-1”
ProductsController: Option 1
❖ Option 1: ProductsService always returns an array
❖ ProductsController has no way …
❖ to “detect that no user was found”
❖ ProductsController sends-out empty array in all cases
❖ Consumer of ProductsController has no way to know
that they passed-in a wrong User ID
❖ and goes-on thinking that “user has no products”
ProductsController: Option 2
❖ Option 2: ProductsService returns -1 when no User found
❖ ProductsController must handle that special-case
❖ Send custom error message “User does not exist”
ProductsController: Option 1
ProductsController: Option 1
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId);
}
ProductsController: Option 1
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId)
//if non-existent userId was passed
//empty array is still returned
}
ProductsController: Option 1
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId)
//if non-existent userId was passed
//empty array is still returned
//API consumer is confused :(
}
ProductsController: Option 2
ProductsController: Option 2
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId);
}
ProductsController: Option 2
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
ProductsController: Option 2
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
ProductsController: Option 2
❖ Pros:
❖ We don’t confuse API consumer when they send a bad
User ID
❖ Cons:
❖ More Code
❖ Mixing User-related code with Products-related code
❖ Controller has to know what “-1” means !!!
Impacts
Impacts
❖ UserRepository could be used by dozens of Services
❖ Each Service will have to “check for null” when no User
❖ And in turn, find a way to “Signal” this upstream …
❖ … to the Controllers with “-1”
❖ and Controllers will have to handle “-1”
❖ … every. time.
Impacts
❖ Now …
❖ Multiply the preceding challenges …
❖ By Dozens of Repositories
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Impacts
❖ Rampant “Status Codes”: -1, -2, -3, -4
❖ to signal a myriad of error conditions
❖ bound to status messages …
❖ maintained in lookup arrays
Impacts
❖ So. Much. Code.
❖ Corners will be cut.
❖ Strange behavior will creep throughout the system.
Exceptions for the Win
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
X
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
UserNotFoundException
UserNotFoundException
❖ class UserNotFoundException extends Exception
{
const MESSAGE = ‘User ID could not be found: ’;
public function __construct(string $userId = “”)
{
parent::__construct(
self::MESSAGE.intval($userId),
404,
null
);
}
}
Exceptions Flow
Exceptions Flow
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
Exceptions Flow
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ throws UserNotFoundException
Exceptions Flow
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ throws UserNotFoundException
Exceptions Flow
❖ UserNotFoundException will “Bubble-Up”.
❖ No code has to catch it.
❖ Framework can output it in a useful way.
Simplifying Code
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
Simplifying ProductsService
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
X
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
$products = $this->productsRepo->getProducts($user);
return $products;
}
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
return $this->productsRepo->getProducts($user);
}
Simplifying ProductsController
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
X
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
return $products;
}
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId);
}
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Benefits of Exceptions
Benefits of Exceptions
❖ Less code to handle error cases
❖ Localize signaling at component level
❖ Encapsulate more information
❖ Promote decoupling: upstream components don’t have to
“know” about downstream error cases
Exceptions Best Practices
Exceptions Best Practices
❖ Catch ORM Exceptions and re-throw your own.
❖ Make “lots” of Custom Exception Classes
❖ Think about Exception hierarchy / taxonomy
❖ avoid reusing same exception w/ different error messages
❖ Embed messaging inside exception.
❖ Name them as specifically as possible.
❖ Include helpful messaging as to what happened.
❖ possibly escape or cast inputs - XSS vulnerabilities
❖ intval($userId)
Catching Polymorphic Exceptions
Catching Polymorphic Exceptions
Example: DoctrineORM
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
ORM::getSingleResult
ORM::getSingleResult
❖ Possible Failure Modes:
❖ No Result was found
❖ More than one Result was found
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
ORM::getSingleResult
❖ getSingleResult can throw either:
❖ NoResultException
❖ NonUniqueResultException
Exception
ORMException
UnexpectedResultException
NonUniqueResultException NoResultException
Polymorphic Exceptions
❖ catch (NoResultException $nre) {
//specific handling of No Result
❖ } catch (UnexpectedResultException $ure) {
//fall-back to any UnexpectedResultException
//other than NoResultException
//which includes NonUniqueResultException
❖ } catch (ORMException $oe) {
//fall-back to any ORMException
//other than UnexpectedResultException
}
Polymorphic Exceptions
❖ catch (NoResultException $nre) {
throw new UserNotFoundException();
❖ } catch (UnexpectedResultException $ure) {
$this->log(‘Unexpected Result: ’.$ure);
❖ } catch (ORMException $oe) {
$this->log(‘ORM Exception: ‘.$oe);
}
Polymorphic Exceptions
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
Polymorphic Exceptions
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
} catch (UnexpectedResultException $ure) {
$this->log(‘This should not happen: ’.$ure);
throw new UnexpectedApplicationException();
}
}
Catching Exceptions with “|”
Catching Exceptions with “|”
❖ Useful when trying to catch multiple exceptions …
❖ … When no common ancestor is available
Catching Exceptions with “|”
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException | NonUniqueResultException $e) {
throw new UserNotFoundException($userId);
}
}
Exceptions & PHPUnit
Exceptions & PHPUnit
❖ Declare that what comes next will trigger an Exception
❖ Trigger the Exception-throwing behavior
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
$this->expectException(UserNotFoundException::class);
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
$this->expectException(UserNotFoundException::class);
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
//I am signaling that an exception will be thrown
$this->expectException(UserNotFoundException::class);
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
//I am signaling that an exception will be thrown
$this->expectException(UserNotFoundException::class);
//I am triggering the exception with $bogusUserId
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
//I am signaling that an exception will be thrown
$this->expectException(UserNotFoundException::class);
//I am triggering the exception with $bogusUserId
$this->userRepo->getUserById($bogusUserId);
}
TDD Exception Kata
/**
* @throws UserNotFoundException
*/
public function testBogusUserIdThrowsException()
{
$legitimateUser = $this->getRepoNewUser();
/** @var Uuid $bogusUserId */
$bogusUserId = Uuid::uuid4();
self::assertNotNull($legitimateUser->getId());
self::assertFalse($legitimateUser->getId()->equals($bogusUserId));
$this->expectException(UserNotFoundException::class);
$this->userRepository->byId($bogusUserId);
}
public function byId(Uuid $id) : AppUser
{
try {
return $this->em->createQuery(
'select u from E:AppUser u where u.id = :id'
)
->setParameter('id', $id)
->getSingleResult();
} catch (NoResultException | NonUniqueResultException $e) {
throw new UserNotFoundException();
}
}
Interested in TDD?
Interested in TDD?
http://bit.ly/tdd-vids
Takeaways
Fail w/ Poise
Signal Failures w/ Less Code
Handle Failures w/ Less Code
Reduce Technical Debt
Build Robust Systems
Exceptions are your Friend
Thank You!
https://twitter.com/chrisholland
Slides:
http://bit.ly/exceptions-ftw

Weitere ähnliche Inhalte

Was ist angesagt?

Javascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionJavascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionIban Martinez
 
Strategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux ApplicaitonsStrategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux Applicaitonsgarbles
 
Coding for Scale and Sanity
Coding for Scale and SanityCoding for Scale and Sanity
Coding for Scale and SanityJimKellerES
 
Data20161007
Data20161007Data20161007
Data20161007capegmail
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyHuiyi Yan
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05cKaz Yoshikawa
 
Sins Against Drupal 2
Sins Against Drupal 2Sins Against Drupal 2
Sins Against Drupal 2Aaron Crosman
 
Continuous Integration: a practical approach
Continuous Integration: a practical approachContinuous Integration: a practical approach
Continuous Integration: a practical approachESUG
 
Command-Oriented Architecture
Command-Oriented ArchitectureCommand-Oriented Architecture
Command-Oriented ArchitectureLuiz Messias
 
How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Componentscagataycivici
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testingsmontanari
 
Deep-dive into Django #1
Deep-dive into Django #1Deep-dive into Django #1
Deep-dive into Django #1Avik Das
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.inJaikant Kumaran
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mockskenbot
 
WordPress Capabilities Magic
WordPress Capabilities MagicWordPress Capabilities Magic
WordPress Capabilities Magicmannieschumpert
 

Was ist angesagt? (20)

Unit testing UIView
Unit testing UIViewUnit testing UIView
Unit testing UIView
 
Javascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionJavascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introduction
 
Rspec and Rails
Rspec and RailsRspec and Rails
Rspec and Rails
 
Strategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux ApplicaitonsStrategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux Applicaitons
 
Coding for Scale and Sanity
Coding for Scale and SanityCoding for Scale and Sanity
Coding for Scale and Sanity
 
Data20161007
Data20161007Data20161007
Data20161007
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journey
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05c
 
Sins Against Drupal 2
Sins Against Drupal 2Sins Against Drupal 2
Sins Against Drupal 2
 
Continuous Integration: a practical approach
Continuous Integration: a practical approachContinuous Integration: a practical approach
Continuous Integration: a practical approach
 
Command-Oriented Architecture
Command-Oriented ArchitectureCommand-Oriented Architecture
Command-Oriented Architecture
 
How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Components
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
Deep-dive into Django #1
Deep-dive into Django #1Deep-dive into Django #1
Deep-dive into Django #1
 
jQuery Presentasion
jQuery PresentasionjQuery Presentasion
jQuery Presentasion
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.in
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mocks
 
Handlebars.js
Handlebars.jsHandlebars.js
Handlebars.js
 
WordPress Capabilities Magic
WordPress Capabilities MagicWordPress Capabilities Magic
WordPress Capabilities Magic
 

Ähnlich wie Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"

Guard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityRyan Weaver
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreRyan Weaver
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにYuya Takeyama
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Introduction to DI(C)
Introduction to DI(C)Introduction to DI(C)
Introduction to DI(C)Radek Benkel
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonKris Wallsmith
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software TestingSergio Arroyo
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...DevClub_lv
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitmfrost503
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitmfrost503
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Web::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPWeb::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPMichael Francis
 
Serial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy APISerial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy APIPiotr Horzycki
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2eugenio pombi
 

Ähnlich wie Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling" (20)

Guard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful Security
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くために
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Introduction to DI(C)
Introduction to DI(C)Introduction to DI(C)
Introduction to DI(C)
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software Testing
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit Testing
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Taming Command Bus
Taming Command BusTaming Command Bus
Taming Command Bus
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Web::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPWeb::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTP
 
Serial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy APISerial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy API
 
BDD de fuera a dentro
BDD de fuera a dentroBDD de fuera a dentro
BDD de fuera a dentro
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2
 

Mehr von Fwdays

"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...Fwdays
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...Fwdays
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...Fwdays
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...Fwdays
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...Fwdays
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...Fwdays
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...Fwdays
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra MyronovaFwdays
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...Fwdays
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...Fwdays
 
"Leadership, Soft Skills, and Personality Types for IT teams", Sergiy Tytenko
"Leadership, Soft Skills, and Personality Types for IT teams",  Sergiy Tytenko"Leadership, Soft Skills, and Personality Types for IT teams",  Sergiy Tytenko
"Leadership, Soft Skills, and Personality Types for IT teams", Sergiy TytenkoFwdays
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...Fwdays
 
"Mastering cross-cultural communication", Anna Gandrabura
"Mastering cross-cultural communication", Anna Gandrabura"Mastering cross-cultural communication", Anna Gandrabura
"Mastering cross-cultural communication", Anna GandraburaFwdays
 
"Incremental rollouts and rollbacks with business metrics control at every st...
"Incremental rollouts and rollbacks with business metrics control at every st..."Incremental rollouts and rollbacks with business metrics control at every st...
"Incremental rollouts and rollbacks with business metrics control at every st...Fwdays
 
"AIRe - AI Reliability Engineering", Denys Vasyliev
"AIRe - AI Reliability Engineering", Denys Vasyliev"AIRe - AI Reliability Engineering", Denys Vasyliev
"AIRe - AI Reliability Engineering", Denys VasylievFwdays
 
"Testing of Helm Charts or There and Back Again", Yura Rochniak
"Testing of Helm Charts or There and Back Again", Yura Rochniak"Testing of Helm Charts or There and Back Again", Yura Rochniak
"Testing of Helm Charts or There and Back Again", Yura RochniakFwdays
 
"Running Open-Source LLM models on Kubernetes", Volodymyr Tsap
"Running Open-Source LLM models on Kubernetes",  Volodymyr Tsap"Running Open-Source LLM models on Kubernetes",  Volodymyr Tsap
"Running Open-Source LLM models on Kubernetes", Volodymyr TsapFwdays
 
"Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O...
"Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O..."Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O...
"Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O...Fwdays
 
"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin
"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin
"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro KozhevinFwdays
 
"Platform Engineering with Development Containers", Igor Fesenko
"Platform Engineering with Development Containers", Igor Fesenko"Platform Engineering with Development Containers", Igor Fesenko
"Platform Engineering with Development Containers", Igor FesenkoFwdays
 

Mehr von Fwdays (20)

"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
 
"Leadership, Soft Skills, and Personality Types for IT teams", Sergiy Tytenko
"Leadership, Soft Skills, and Personality Types for IT teams",  Sergiy Tytenko"Leadership, Soft Skills, and Personality Types for IT teams",  Sergiy Tytenko
"Leadership, Soft Skills, and Personality Types for IT teams", Sergiy Tytenko
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"Mastering cross-cultural communication", Anna Gandrabura
"Mastering cross-cultural communication", Anna Gandrabura"Mastering cross-cultural communication", Anna Gandrabura
"Mastering cross-cultural communication", Anna Gandrabura
 
"Incremental rollouts and rollbacks with business metrics control at every st...
"Incremental rollouts and rollbacks with business metrics control at every st..."Incremental rollouts and rollbacks with business metrics control at every st...
"Incremental rollouts and rollbacks with business metrics control at every st...
 
"AIRe - AI Reliability Engineering", Denys Vasyliev
"AIRe - AI Reliability Engineering", Denys Vasyliev"AIRe - AI Reliability Engineering", Denys Vasyliev
"AIRe - AI Reliability Engineering", Denys Vasyliev
 
"Testing of Helm Charts or There and Back Again", Yura Rochniak
"Testing of Helm Charts or There and Back Again", Yura Rochniak"Testing of Helm Charts or There and Back Again", Yura Rochniak
"Testing of Helm Charts or There and Back Again", Yura Rochniak
 
"Running Open-Source LLM models on Kubernetes", Volodymyr Tsap
"Running Open-Source LLM models on Kubernetes",  Volodymyr Tsap"Running Open-Source LLM models on Kubernetes",  Volodymyr Tsap
"Running Open-Source LLM models on Kubernetes", Volodymyr Tsap
 
"Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O...
"Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O..."Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O...
"Crisis to Calm: Incident Management’s Role in Business Stability", Oleksii O...
 
"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin
"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin
"DevOps Practisting Platform on EKS with Karpenter autoscaling", Dmytro Kozhevin
 
"Platform Engineering with Development Containers", Igor Fesenko
"Platform Engineering with Development Containers", Igor Fesenko"Platform Engineering with Development Containers", Igor Fesenko
"Platform Engineering with Development Containers", Igor Fesenko
 

Kürzlich hochgeladen

Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsSeth Reyes
 
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDEADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDELiveplex
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfinfogdgmi
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UbiTrack UK
 
UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5DianaGray10
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024SkyPlanner
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesMd Hossain Ali
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?IES VE
 
100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimization100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimizationarrow10202532yuvraj
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1DianaGray10
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Websitedgelyza
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7DianaGray10
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxMatsuo Lab
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostMatt Ray
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaborationbruanjhuli
 

Kürzlich hochgeladen (20)

Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and Hazards
 
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDEADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdf
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
 
UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024
 
20230104 - machine vision
20230104 - machine vision20230104 - machine vision
20230104 - machine vision
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?
 
100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimization100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimization
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Website
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptx
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
 

Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"

  • 2. Hello, From Austin, Texas, USA
  • 8. Validations: Painful ❖ if ( $this->validateThingOne() && $this->validateThingTwo() && $this->validateThingThree() ) { doSomeTransaction(); return true; //signal that the thing was done } else { return false; //signal the thing was not done }
  • 9. Validations: Painful ❖ private function validateThingOne() { if (//someErrorCondition) { return false; } else { return true; } }
  • 10. Validations: Painful ❖ private function validateThingTwo() { if (//someErrorCondition) { return false; } else { return true; } }
  • 11. Validations: Painful ❖ private function validateThingThree() { if (//someErrorCondition) { return false; } else { return true; } }
  • 12. Validations: Painful ❖ Validation methods must “return true / false” … ❖ to signal success or failure ❖ upstream methods must adapt behavior … ❖ based on true / false ❖ Code has more nesting
  • 16. Validations: Graceful ❖ $this->validateThingOne(); $this->validateThingTwo(); $this->validateThingThree(); //if any exception thrown above //doSomeTransaction() will not run doSomeTransaction();
  • 17. Validations: Graceful ❖ private function validateThingOne() { if (//someErrorCondition) { throw new InvalidThingOneException(); } }
  • 18. Validations: Graceful ❖ private function validateThingTwo() { if (//someErrorCondition) { throw new InvalidThingTwoException(); } }
  • 19. Validations: Graceful ❖ private function validateThingThree() { if (//someErrorCondition) { throw new InvalidThingThreeException(); } }
  • 20. Validations: Graceful ❖ Validation methods don’t need to return anything ❖ they only need to “throw Exception” on failure ❖ upstream methods don’t have to care ❖ Code has far less nesting.
  • 23. Get Products for User ❖ Failure Modes: ❖ User has no products … ❖ That’s okay ❖ User could not be found? ❖ That’s not normal
  • 24. Possible Stack ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ ProductRepository::getProducts(User $user) : array
  • 25. Possible Stack ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ ProductRepository::getProducts(User $user) : array
  • 26. Possible Stack ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ ProductRepository::getProducts(User $user) : array
  • 28. getUserById(int $userId) : User ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); }
  • 29. getUserById(int $userId) : User ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); }
  • 30. getUserById(int $userId) : User ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } What if Bogus?
  • 31. What Happens When … … User Not Found?
  • 33. getUserById(int $userId) : User ❖ What happens when no user is found? ❖ return null? ❖ return false? ❖ return -1?
  • 35. UserRepository ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); }
  • 36. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } }
  • 37. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } }
  • 38. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; //no Result was found, returning null } }
  • 40. ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 41. ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 42. ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } //we don’t want to send a ‘null’ $user to getProducts $products = $this->productsRepo->getProducts($user); return $products; }
  • 43. ProductsService ❖ If no user is found … ❖ I return an empty array … of Products ❖ … because it feels convenient ❖ is this, however, elegant?
  • 44. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products ❖ no user was found
  • 45. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products <— That’s OK ❖ no user was found
  • 46. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products <— That’s OK ❖ no user was found
  • 47. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products <— That’s OK ❖ no user was found <— strange behavior
  • 49. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 50. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; } X
  • 51. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 52. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; //using -1 to “signal” no user found } $products = $this->productsRepo->getProducts($user); return $products; }
  • 53. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; //using -1 to “signal” no user found } $products = $this->productsRepo->getProducts($user); return $products; }
  • 54. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 55. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; } $products = $this->productsRepo->getProducts($user); return $products; } Inconsistent Return Types !
  • 56. ProductsService: Alternative ❖ I return “-1” to “signal” that no user could be found ❖ so my Service can now either return: ❖ an array of products ❖ or -1 ❖ … that’s not … “great”.
  • 58. ProductsController ❖ Based on what the Service returns, the Controller has two options: ❖ Option 1: also always return an array ❖ Option 2: handle “-1”
  • 59. ProductsController: Option 1 ❖ Option 1: ProductsService always returns an array ❖ ProductsController has no way … ❖ to “detect that no user was found” ❖ ProductsController sends-out empty array in all cases ❖ Consumer of ProductsController has no way to know that they passed-in a wrong User ID ❖ and goes-on thinking that “user has no products”
  • 60. ProductsController: Option 2 ❖ Option 2: ProductsService returns -1 when no User found ❖ ProductsController must handle that special-case ❖ Send custom error message “User does not exist”
  • 62. ProductsController: Option 1 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId); }
  • 63. ProductsController: Option 1 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId) //if non-existent userId was passed //empty array is still returned }
  • 64. ProductsController: Option 1 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId) //if non-existent userId was passed //empty array is still returned //API consumer is confused :( }
  • 66. ProductsController: Option 2 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId); }
  • 67. ProductsController: Option 2 ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } }
  • 68. ProductsController: Option 2 ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } }
  • 69. ProductsController: Option 2 ❖ Pros: ❖ We don’t confuse API consumer when they send a bad User ID ❖ Cons: ❖ More Code ❖ Mixing User-related code with Products-related code ❖ Controller has to know what “-1” means !!!
  • 71. Impacts ❖ UserRepository could be used by dozens of Services ❖ Each Service will have to “check for null” when no User ❖ And in turn, find a way to “Signal” this upstream … ❖ … to the Controllers with “-1” ❖ and Controllers will have to handle “-1” ❖ … every. time.
  • 72. Impacts ❖ Now … ❖ Multiply the preceding challenges … ❖ By Dozens of Repositories
  • 74. Impacts ❖ Rampant “Status Codes”: -1, -2, -3, -4 ❖ to signal a myriad of error conditions ❖ bound to status messages … ❖ maintained in lookup arrays
  • 75. Impacts ❖ So. Much. Code. ❖ Corners will be cut. ❖ Strange behavior will creep throughout the system.
  • 78. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } }
  • 79. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } } X
  • 80. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 82. UserNotFoundException ❖ class UserNotFoundException extends Exception { const MESSAGE = ‘User ID could not be found: ’; public function __construct(string $userId = “”) { parent::__construct( self::MESSAGE.intval($userId), 404, null ); } }
  • 84. Exceptions Flow ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User
  • 85. Exceptions Flow ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ throws UserNotFoundException
  • 86. Exceptions Flow ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ throws UserNotFoundException
  • 87. Exceptions Flow ❖ UserNotFoundException will “Bubble-Up”. ❖ No code has to catch it. ❖ Framework can output it in a useful way.
  • 89. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 91. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 92. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; } X
  • 93. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); $products = $this->productsRepo->getProducts($user); return $products; }
  • 94. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); return $this->productsRepo->getProducts($user); }
  • 96. Simplifying ProductsController ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } }
  • 97. Simplifying ProductsController ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } } X
  • 98. Simplifying ProductsController ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); return $products; }
  • 99. Simplifying ProductsController ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId); }
  • 102. Benefits of Exceptions ❖ Less code to handle error cases ❖ Localize signaling at component level ❖ Encapsulate more information ❖ Promote decoupling: upstream components don’t have to “know” about downstream error cases
  • 104. Exceptions Best Practices ❖ Catch ORM Exceptions and re-throw your own. ❖ Make “lots” of Custom Exception Classes ❖ Think about Exception hierarchy / taxonomy ❖ avoid reusing same exception w/ different error messages ❖ Embed messaging inside exception. ❖ Name them as specifically as possible. ❖ Include helpful messaging as to what happened. ❖ possibly escape or cast inputs - XSS vulnerabilities ❖ intval($userId)
  • 107. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 108. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 110. ORM::getSingleResult ❖ Possible Failure Modes: ❖ No Result was found ❖ More than one Result was found
  • 111. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 112. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 113. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 114. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 115. ORM::getSingleResult ❖ getSingleResult can throw either: ❖ NoResultException ❖ NonUniqueResultException
  • 117. Polymorphic Exceptions ❖ catch (NoResultException $nre) { //specific handling of No Result ❖ } catch (UnexpectedResultException $ure) { //fall-back to any UnexpectedResultException //other than NoResultException //which includes NonUniqueResultException ❖ } catch (ORMException $oe) { //fall-back to any ORMException //other than UnexpectedResultException }
  • 118. Polymorphic Exceptions ❖ catch (NoResultException $nre) { throw new UserNotFoundException(); ❖ } catch (UnexpectedResultException $ure) { $this->log(‘Unexpected Result: ’.$ure); ❖ } catch (ORMException $oe) { $this->log(‘ORM Exception: ‘.$oe); }
  • 119. Polymorphic Exceptions ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 120. Polymorphic Exceptions ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } catch (UnexpectedResultException $ure) { $this->log(‘This should not happen: ’.$ure); throw new UnexpectedApplicationException(); } }
  • 122. Catching Exceptions with “|” ❖ Useful when trying to catch multiple exceptions … ❖ … When no common ancestor is available
  • 123. Catching Exceptions with “|” ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException | NonUniqueResultException $e) { throw new UserNotFoundException($userId); } }
  • 125. Exceptions & PHPUnit ❖ Declare that what comes next will trigger an Exception ❖ Trigger the Exception-throwing behavior
  • 126. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; $this->expectException(UserNotFoundException::class); $this->userRepo->getUserById($bogusUserId); }
  • 127. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; $this->expectException(UserNotFoundException::class); $this->userRepo->getUserById($bogusUserId); }
  • 128. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; //I am signaling that an exception will be thrown $this->expectException(UserNotFoundException::class); $this->userRepo->getUserById($bogusUserId); }
  • 129. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; //I am signaling that an exception will be thrown $this->expectException(UserNotFoundException::class); //I am triggering the exception with $bogusUserId $this->userRepo->getUserById($bogusUserId); }
  • 130. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; //I am signaling that an exception will be thrown $this->expectException(UserNotFoundException::class); //I am triggering the exception with $bogusUserId $this->userRepo->getUserById($bogusUserId); }
  • 132. /** * @throws UserNotFoundException */ public function testBogusUserIdThrowsException() { $legitimateUser = $this->getRepoNewUser(); /** @var Uuid $bogusUserId */ $bogusUserId = Uuid::uuid4(); self::assertNotNull($legitimateUser->getId()); self::assertFalse($legitimateUser->getId()->equals($bogusUserId)); $this->expectException(UserNotFoundException::class); $this->userRepository->byId($bogusUserId); }
  • 133. public function byId(Uuid $id) : AppUser { try { return $this->em->createQuery( 'select u from E:AppUser u where u.id = :id' ) ->setParameter('id', $id) ->getSingleResult(); } catch (NoResultException | NonUniqueResultException $e) { throw new UserNotFoundException(); } }
  • 138. Signal Failures w/ Less Code
  • 139. Handle Failures w/ Less Code