Escaping OOP boundaries

Escaping OOP boundaries

Most of developers are constantly teaching and discussing modern patterns, frameworks and libraries. But what if you think that you know almost everything about traditional OOP in PHP? I can bet that at some level of mastery you could notice that traditional object-oriented patterns do not solve all problems but introduce new questions instead. This is because OOP-way is not suited well for complex tasks.

Are you looking for the new food for thoughts about how such complex issues could be solved in PHP? For example how to make your existing method asynchronous just with one single word? Or how to reduce boilerplate code for feature toggles?

Join me and I will show you how to apply the most powerful aspect-oriented framework to escape from your existing OOP boundaries and teach you new patterns that can help you to keep your code clean.

E7af3ef8e48a800cd2eedae073104c1a?s=128

Alexander Lisachenko

November 09, 2019
Tweet

Transcript

  1. Escaping from OOP boundaries Alexander Lisachenko

  2. About me: 2 lisachenko lisachenko

  3. About me: ‣ Head of Web Development and Architecture at

    Alpari (RU) Forex Broker 2 lisachenko lisachenko
  4. About me: ‣ Head of Web Development and Architecture at

    Alpari (RU) Forex Broker ‣ Have worked with computers since 7 years 2 lisachenko lisachenko
  5. About me: ‣ Head of Web Development and Architecture at

    Alpari (RU) Forex Broker ‣ Have worked with computers since 7 years ‣ Clean code advocate, guru in the Enterprise Architecture 2 lisachenko lisachenko
  6. About me: ‣ Head of Web Development and Architecture at

    Alpari (RU) Forex Broker ‣ Have worked with computers since 7 years ‣ Clean code advocate, guru in the Enterprise Architecture ‣ Author of the aspect-oriented framework Go! AOP 
 http://go.aopphp.com 2 lisachenko lisachenko
  7. 3 Moscow PHP User Group

  8. 3 Moscow PHP User Group

  9. Agenda: 4

  10. Agenda: ‣ Object-Oriented Paradigm boundaries 4

  11. Agenda: ‣ Object-Oriented Paradigm boundaries ‣ Escaping from PHP boundaries.

    4
  12. Agenda: ‣ Object-Oriented Paradigm boundaries ‣ Escaping from PHP boundaries.

    ‣ Stream wrapper and filters. 4
  13. Agenda: ‣ Object-Oriented Paradigm boundaries ‣ Escaping from PHP boundaries.

    ‣ Stream wrapper and filters. ‣ Cross-Cutting concerns. 4
  14. Agenda: ‣ Object-Oriented Paradigm boundaries ‣ Escaping from PHP boundaries.

    ‣ Stream wrapper and filters. ‣ Cross-Cutting concerns. ‣ Aspect-Oriented Programming with Go! AOP 4
  15. Agenda: ‣ Object-Oriented Paradigm boundaries ‣ Escaping from PHP boundaries.

    ‣ Stream wrapper and filters. ‣ Cross-Cutting concerns. ‣ Aspect-Oriented Programming with Go! AOP ‣ Hacking the PHP. Native structures and opcodes. 4
  16. 5 OOP Boundaries

  17. 6 Boundary 1: Open - Closed Principle

  18. 6 Boundary 1: Open - Closed Principle

  19. Class is Open for extension, but Closed for modification 7

  20. 8 Final class prevents inheritance

  21. 8 Final class prevents inheritance

  22. 9 Final class prevents inheritance

  23. 9 Final class prevents inheritance

  24. 9 Final class prevents inheritance Fatal error: Class Child may

    not inherit from final class (Secret)
  25. 10 Final method prevents overriding

  26. 10 Final method prevents overriding

  27. 11 Final method prevents overriding

  28. 11 Final method prevents overriding

  29. 11 Final method prevents overriding Fatal error: Cannot override final

    method Secret::test()
  30. 11 Final method prevents overriding Fatal error: Cannot override final

    method Secret::test()
  31. 12 Private modifier restricts access

  32. 12 Private modifier restricts access

  33. 13 Private modifier restricts access

  34. 13 Private modifier restricts access

  35. 14 Private modifier restricts access Uncaught Error: Call to private

    method Secret::test()
  36. 15 Boundary 2: Immutable PHP structures

  37. 15 Boundary 2: Immutable PHP structures

  38. 16 PHP

  39. 16 PHP Interpreter?

  40. 16 PHP Interpreter? Compiler?

  41. 16 PHP Interpreter? Compiler? BOTH!

  42. 17 PHP Script

  43. 17 PHP Script Execute

  44. 17 PHP Script Parse Execute

  45. 17 PHP Script Parse Execute Compile to OpCodes

  46. 17 PHP Script Parse Execute Compile to OpCodes Execute(ZendEngine)

  47. 17 PHP Script Parse Execute Compile to OpCodes Execute(ZendEngine) Output

  48. 17 PHP Script Execute Execute(ZendEngine) Output Load OpCache <- Opcodes

  49. Once compiled into Opcodes, code can not be changed in

    runtime 18
  50. 19 OpCache:

  51. 19 OpCache: +Fixed-size shared memory-block

  52. 19 OpCache: +Fixed-size shared memory-block +Contains PHP binary structures

  53. 19 OpCache: +Fixed-size shared memory-block +Contains PHP binary structures +Structures

    are immutable
  54. 19 OpCache: +Fixed-size shared memory-block +Contains PHP binary structures +Structures

    are immutable +Append-only
  55. 20 OpCache shared immutable data:

  56. 20 OpCache shared immutable data: +File functions (HashTable)

  57. 20 OpCache shared immutable data: +File functions (HashTable) +File classes

    (HashTable)
  58. 20 OpCache shared immutable data: +File functions (HashTable) +File classes

    (HashTable) +File main OpArray
  59. 20 OpCache shared immutable data: +File functions (HashTable) +File classes

    (HashTable) +File main OpArray +Interned strings and immutable arrays
  60. 20 OpCache shared immutable data: +File functions (HashTable) +File classes

    (HashTable) +File main OpArray +Interned strings and immutable arrays +Meta-information (system_id, etc)
  61. Shared immutable OpCache improves PHP performance a lot 21

  62. 22 OpCache OOP restrictions:

  63. 22 OpCache OOP restrictions: - Method could not be added/deleted

    or changed in runtime
  64. 22 OpCache OOP restrictions: - Method could not be added/deleted

    or changed in runtime - Class, its parent, implemented interfaces and used traits are declared in compile-time, runtime API is not available
  65. 22 OpCache OOP restrictions: - Method could not be added/deleted

    or changed in runtime - Class, its parent, implemented interfaces and used traits are declared in compile-time, runtime API is not available - Low-level access to the memory is restricted to prevents errors and memory leaks
  66. 22 PS. Possible only with extensions like runkit, uopz, etc

    OpCache OOP restrictions: - Method could not be added/deleted or changed in runtime - Class, its parent, implemented interfaces and used traits are declared in compile-time, runtime API is not available - Low-level access to the memory is restricted to prevents errors and memory leaks
  67. 23 Escaping from OOP Boundaries

  68. Сan we mock/extend a final class or override a final

    method? 24
  69. Сan we mock/extend a final class or override a final

    method? 24 Yes?
  70. Сan we mock/extend a final class or override a final

    method? 24 Yes? No?
  71. The simplest solution is to not mark classes or methods

    as final! 25
  72. 26 Escape: uopz extension

  73. «uopz is a black magic extension of the runkit-and-scary-stuff genre,

    intended to help with QA infrastructure. 27 Joe Watkins
  74. 28 Mocking a final class - uopz extension

  75. 28 Mocking a final class - uopz extension

  76. 28 Mocking a final class - uopz extension

  77. 29 Escape: Replace the «file» stream wrapper

  78. 30 Idea:

  79. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

  80. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

    ‣ We can unregister the «file» handler via stream_wrapper_unregister(‘file’)
  81. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

    ‣ We can unregister the «file» handler via stream_wrapper_unregister(‘file’) ‣ Register our own file handler via stream_wrapper_register(‘file’, self::class)
  82. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

    ‣ We can unregister the «file» handler via stream_wrapper_unregister(‘file’) ‣ Register our own file handler via stream_wrapper_register(‘file’, self::class) ‣ Can hook into the PHP source file now!
  83. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

    ‣ We can unregister the «file» handler via stream_wrapper_unregister(‘file’) ‣ Register our own file handler via stream_wrapper_register(‘file’, self::class) ‣ Can hook into the PHP source file now! ‣ Tokenise code with token_get_all($source)
  84. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

    ‣ We can unregister the «file» handler via stream_wrapper_unregister(‘file’) ‣ Register our own file handler via stream_wrapper_register(‘file’, self::class) ‣ Can hook into the PHP source file now! ‣ Tokenise code with token_get_all($source) ‣ Transform this source (eg. remove final keyword)
  85. 30 Idea: ‣ PHP uses internal «file» wrapper for includes

    ‣ We can unregister the «file» handler via stream_wrapper_unregister(‘file’) ‣ Register our own file handler via stream_wrapper_register(‘file’, self::class) ‣ Can hook into the PHP source file now! ‣ Tokenise code with token_get_all($source) ‣ Transform this source (eg. remove final keyword) ‣ Give it back to the PHP to compile
  86. 31 dg/bypass-finals library composer show dg/bypass-finals name : dg/bypass-finals descrip.

    : Removes final keyword from source code on-the- fly and allows mocking of final methods and classes keywords : testing, phpunit, unit, mocking, finals versions : dev-master, v1.1.2, v1.1.1, v1.1.0, v1.0.1, v1.0 type : library
  87. 32 Usage:

  88. 32 Usage: You need to enable it before the classes

    you want to remove the final are loaded. So call it as soon as possible!
  89. 32 Usage: You need to enable it before the classes

    you want to remove the final are loaded. So call it as soon as possible! PS. For additional details about PhpUnit integration, please read: https://www.tomasvotruba.cz/blog/2019/03/28/how-to-mock-final-classes-in-phpunit/
  90. 33 antecedent/patchwork library composer show antecedent/patchwork name : antecedent/patchwork descrip.

    : Method redefinition (monkey-patching) functionality for PHP. keywords : aop, testing, aspect, runkit, redefinition, monkeypatching, interception versions : dev-master, 2.1.11, 2.1.10… type : library
  91. Write tests for your PHP code, not code for the

    tests. 34
  92. 35 Escape: Use PHP stream filters with includes

  93. 36 Simple test file:

  94. 36 Simple test file:

  95. 37 Let’s craft our special include:

  96. 37 Let’s craft our special include:

  97. 38 It is become upper-cased:

  98. 38 It is become upper-cased:

  99. Idea: we can register our own stream filter via stream_filter_register()

    to process a source code. 39
  100. Cross-cutting concerns

  101. Cross-cutting concerns Data layer Service layer Controller layer

  102. Cross-cutting concerns Data layer Service layer Controller layer Authorization Logging

    Caching Transaction Control
  103. Cross-cutting concerns are aspects of a program that affect other

    domains. 41
  104. Why? 42

  105. Limitation of object-oriented representation of real life. 43

  106. Joe Armstrong “…You wanted a banana but what you got

    was a gorilla holding the banana and the entire jungle” 44
  107. Joe Armstrong “…You wanted a banana but what you got

    was a gorilla holding the banana and the entire jungle” 44
  108. Example: 45

  109. Example: 45 Implement an audit system that checks an access

    rights for each public method in all classes in our system and then logs this information into the security journal.
  110. Example: 46

  111. Example: 46

  112. Authorization: 47

  113. Authorization: 47

  114. Logging and audit: 48

  115. Logging and audit: 48

  116. Error handling 49

  117. Error handling 49

  118. Code tangling 50

  119. Code tangling 50

  120. Code tangling 50

  121. Code scattering (duplication) 51

  122. Code scattering (duplication) 51

  123. Code scattering (duplication) 51

  124. Cross-cutting concerns 52

  125. 53 Cross-cutting concerns

  126. Aspect-Oriented Paradigm

  127. Idea of AOP: extract cross-cutting concerns from general classes into

    the separate entities called aspects. 55
  128. Authorization concern: 56

  129. Authorization concern: 56

  130. Authorization aspect: 57

  131. Authorization aspect: 57

  132. Authorization aspect: 57 Advice - event handler

  133. Authorization aspect: 57 Pointcut - describes list of interesting events

    Advice - event handler
  134. Authorization aspect: 57 Pointcut - describes list of interesting events

    Advice - event handler Joinpoint - defines an event object
  135. 58 Aspect VS Event Listener

  136. 58 Aspect VS Event Listener

  137. 58 Aspect VS Event Listener

  138. 58 Aspect VS Event Listener

  139. 59 Advice types

  140. 59 Advice types «Before» type

  141. 59 Advice types «Before» type «After» type

  142. 59 Advice types «Before» type «After» type «Around» type

  143. With AOP we can subscribe to any method without changing

    original code. 60
  144. 61 goaop/framework composer show goaop/framework name : goaop/framework descrip. :

    Framework for aspect-oriented programming in PHP. keywords : php, aop, library, aspect versions : 3.0.x-dev, 2.3.2,… type : library https://github.com/goaop/framework
  145. How it works? 62

  146. How it works? 62

  147. 63 We use our trick with php://filter stream filter

  148. 64 The composer class loader is replaced with a weaving

    proxy
  149. 65 Lexical analysis and parsing of source code into the

    AST is performed (nikic/PHP-Parser)
  150. 66 Static reflection is created from the AST (goaop/parser-reflection)

  151. 66 Static reflection is created from the AST (goaop/parser-reflection)

  152. 66 Static reflection is created from the AST (goaop/parser-reflection)

  153. 66 Static reflection is created from the AST (goaop/parser-reflection)

  154. 67 The original class is renamed and replaced with a

    new class with additional behavior; stored in the cache
  155. 67 The original class is renamed and replaced with a

    new class with additional behavior; stored in the cache
  156. 67 The original class is renamed and replaced with a

    new class with additional behavior; stored in the cache Same class name!
  157. 67 The original class is renamed and replaced with a

    new class with additional behavior; stored in the cache Original class renamed Same class name!
  158. 67 The original class is renamed and replaced with a

    new class with additional behavior; stored in the cache Original class renamed Overridden method Same class name!
  159. 68 Deferred methods Runtime transformation of methods into deferred methods

  160. 68 Deferred methods Runtime transformation of methods into deferred methods

  161. 69 Example code

  162. 69 Example code Some long service call

  163. 69 Example code

  164. 70 Idea: prevent execution of methods wrapping them into promises

    and run after the fastcgi_finish_request.
  165. 71 Define an annotation-marker

  166. 72 Define an aspect

  167. 72 Define an aspect

  168. 72 Define an aspect All methods with «Async» annotation

  169. 72 Define an aspect Record each call with arguments

  170. 72 Define an aspect Prevent execution of original method

  171. 72 Define an aspect Return «our» value

  172. 73 Define an aspect

  173. 73 Define an aspect

  174. 73 Define an aspect

  175. 73 Define an aspect

  176. 73 Define an aspect Finish FASTCGI Request

  177. 73 Define an aspect Execute delayed methods

  178. 74 Declare method as deferred

  179. 74 Declare method as deferred

  180. 74 Declare method as deferred Now it will be deferred

  181. Hacking the PHP engine

  182. Hacking the PHP engine

  183. None
  184. 77 FFI - Foreign Function Interface

  185. 77 ‣ Available from PHP>=7.4 FFI - Foreign Function Interface

  186. 77 ‣ Available from PHP>=7.4 ‣ Allows calling C functions

    FFI - Foreign Function Interface
  187. 77 ‣ Available from PHP>=7.4 ‣ Allows calling C functions

    ‣ Allows using C data types from pure scripting language FFI - Foreign Function Interface
  188. 77 ‣ Available from PHP>=7.4 ‣ Allows calling C functions

    ‣ Allows using C data types from pure scripting language ‣ Opens a way to write PHP extensions and bindings to C libraries in pure PHP FFI - Foreign Function Interface
  189. 77 ‣ Available from PHP>=7.4 ‣ Allows calling C functions

    ‣ Allows using C data types from pure scripting language ‣ Opens a way to write PHP extensions and bindings to C libraries in pure PHP ‣ Headers can be preloaded during server startup FFI - Foreign Function Interface
  190. 78 FFI - Foreign Function Interface

  191. Idea: what if we use PHP’s FFI to access… 79

  192. Idea: what if we use PHP’s FFI to access… 79

    … PHP itself!
  193. Idea: what if we use PHP’s FFI to access… 79

    … PHP itself!
  194. 80 lisachenko/z-engine composer show lisachenko/z-engine name : lisachenko/z-engine descrip. :

    Library that provides direct access to native PHP structures. versions : dev-master, 0.5.0,… type : library https://github.com/lisachenko/z-engine
  195. 81 lisachenko/z-engine

  196. 81 ‣ Build on top of PHP’s Reflection API, eg

    ReflectionClass, ReflectionMethod lisachenko/z-engine
  197. 81 ‣ Build on top of PHP’s Reflection API, eg

    ReflectionClass, ReflectionMethod ‣ Provides low-level binding to PHP’s structures like zval, zend_class_entry, zend_function and much more… lisachenko/z-engine
  198. 81 ‣ Build on top of PHP’s Reflection API, eg

    ReflectionClass, ReflectionMethod ‣ Provides low-level binding to PHP’s structures like zval, zend_class_entry, zend_function and much more… ‣ Manipulates PHP itself in runtime lisachenko/z-engine
  199. 81 ‣ Build on top of PHP’s Reflection API, eg

    ReflectionClass, ReflectionMethod ‣ Provides low-level binding to PHP’s structures like zval, zend_class_entry, zend_function and much more… ‣ Manipulates PHP itself in runtime ‣ Expect memory leaks and segfaults! lisachenko/z-engine
  200. 82 Un-final class

  201. 82 Un-final class

  202. 82 Un-final class

  203. 82 Un-final class

  204. 83 Serializable Closure class

  205. 83 Serializable Closure class

  206. 84 Serializable Closure class

  207. 84 Serializable Closure class

  208. 85 Custom Throwable objects

  209. 86 Custom Throwable objects

  210. 86 Custom Throwable objects

  211. 87 Native assembly methods in PHP

  212. 87 Native assembly methods in PHP

  213. 88 First ever assembly method in PHP

  214. 89 What to expect:

  215. 89 ‣ API for userland PHP extensions What to expect:

  216. 89 ‣ API for userland PHP extensions ‣ OpCode manipulation

    and transformation What to expect:
  217. 89 ‣ API for userland PHP extensions ‣ OpCode manipulation

    and transformation ‣ Shared objects (will survive between requests) What to expect:
  218. 89 ‣ API for userland PHP extensions ‣ OpCode manipulation

    and transformation ‣ Shared objects (will survive between requests) ‣ Native hooks for PHP callbacks What to expect:
  219. 89 ‣ API for userland PHP extensions ‣ OpCode manipulation

    and transformation ‣ Shared objects (will survive between requests) ‣ Native hooks for PHP callbacks ‣ Much more :) What to expect:
  220. None
  221. None
  222. Thank you for the attention! https://github.com/goaop https://github.com/lisachenko https://twitter.com/lisachenko Leave feedback

    at: https://joind.in/talk/310fd
  223. Go! AOP: Plugin for the PhpStorm 92

  224. Pointcut syntax highlighting, completion and analysis 93

  225. Pointcut syntax highlighting, completion and analysis 93

  226. Navigate to advice/advised elements 94

  227. Navigate to advice/advised elements 94