├── .gitignore ├── Book ├── Makefile ├── TODO │ ├── constants.rst │ ├── creating_ext.rst │ ├── final.rst │ ├── functions.rst │ ├── ini.rst │ ├── managing_memory.rst │ ├── resources.rst │ ├── sapis.rst │ ├── streams.rst │ └── zend_engine.rst ├── _static │ ├── .gitignore │ └── style.css ├── _templates │ ├── .gitignore │ └── layout.html ├── build_system.rst ├── build_system │ ├── building_extensions.rst │ └── building_php.rst ├── classes_objects.rst ├── classes_objects │ ├── custom_object_storage.rst │ ├── implementing_typed_arrays.rst │ ├── internal_structures_and_implementation.rst │ ├── iterators.rst │ ├── magic_interfaces_comparable.rst │ ├── object_handlers.rst │ ├── serialization.rst │ └── simple_classes.rst ├── conf.py ├── hashtables.rst ├── hashtables │ ├── array_api.rst │ ├── basic_structure.rst │ ├── hash_algorithm.rst │ ├── hashtable_api.rst │ └── images │ │ ├── basic_hashtable.svg │ │ ├── doubly_linked_hashtable.svg │ │ └── ordered_hashtable.svg ├── index.rst ├── introduction.rst ├── php7 │ ├── build_system.rst │ ├── build_system │ │ ├── building_extensions.rst │ │ └── building_php.rst │ ├── extensions_design.rst │ ├── extensions_design │ │ ├── globals_management.rst │ │ ├── images │ │ │ ├── php_classic_lifetime.png │ │ │ ├── php_extensions_lifecycle.png │ │ │ ├── php_lifetime.png │ │ │ ├── php_lifetime_process.png │ │ │ └── php_lifetime_thread.png │ │ └── php_lifecycle.rst │ ├── internal_types.rst │ ├── internal_types │ │ ├── hashtables.rst │ │ ├── strings.rst │ │ ├── strings │ │ │ ├── images │ │ │ │ └── zend_string_memory_layout.png │ │ │ ├── smart_str.rst │ │ │ └── zend_strings.rst │ │ ├── zend_resources.rst │ │ ├── zvals.rst │ │ └── zvals │ │ │ └── basic_structure.rst │ ├── introduction.rst │ ├── memory_management.rst │ └── memory_management │ │ ├── memory_debugging.rst │ │ └── zend_memory_manager.rst ├── zvals.rst └── zvals │ ├── basic_structure.rst │ ├── casts_and_operations.rst │ └── memory_management.rst ├── LICENSE.md ├── README.md ├── build_html.sh ├── build_release_html.sh └── build_release_latex.sh /.gitignore: -------------------------------------------------------------------------------- 1 | BookHTML/ 2 | BookLatex/ 3 | 4 | .idea/ -------------------------------------------------------------------------------- /Book/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = ../BookHTML 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PHPInternalsBook.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PHPInternalsBook.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PHPInternalsBook" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PHPInternalsBook" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /Book/TODO/constants.rst: -------------------------------------------------------------------------------- 1 | Constants 2 | ========= 3 | 4 | You've learnt how to deal with variables, here we'll introduce an important 5 | concept : constants. Fortunately, they are much more easier to master than 6 | variables. You should know the concepts behind constants as they are much 7 | used in PHP 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | .. 15 | constants/structure.rst 16 | constants/main_api.rst 17 | constants/compile_time_substitution.rst 18 | -------------------------------------------------------------------------------- /Book/TODO/creating_ext.rst: -------------------------------------------------------------------------------- 1 | Creating PHP extensions 2 | ======================= 3 | 4 | You have know enough knowledge to begin creating your first extension. We’ll 5 | reuse all you’ve learnt so far and turn it into a practical case. We’ll then 6 | start writing new PHP function in C and then cover the PHP functions scope from 7 | an internal point of view. 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | .. 15 | creating_ext/zendext_vs_ext.rst 16 | creating_ext/load_mechanism.rst 17 | creating_ext/automatic_tools.rst 18 | creating_ext/building_activating.rst 19 | creating_ext/lifecycle.rst 20 | creating_ext/my_first_ext.rst 21 | -------------------------------------------------------------------------------- /Book/TODO/final.rst: -------------------------------------------------------------------------------- 1 | Final thoughts 2 | ============== 3 | 4 | Here is the content we couldn't find a dedicated chapter for. We'll recall 5 | you about PHP's history, the wonderfull path it's drawn, the different 6 | contributors that helped with it... Then we'll help you to get involved in 7 | PHP internal development by giving you hints on how to help us make 8 | this awesome project grow and master the Web. 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | .. 16 | final/history.rst 17 | final/contributors.rst 18 | final/getting_help.rst 19 | final/contributing.rst 20 | -------------------------------------------------------------------------------- /Book/TODO/functions.rst: -------------------------------------------------------------------------------- 1 | Functions 2 | ========= 3 | 4 | You’ve just built your first extension, and it works ! That’s great ! Now we 5 | will dive deeper into a very important concept : PHP functions. We’ll cover the 6 | subject showing how to declare functions, how to accept parameters from them and 7 | how to make them return values. 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | .. 15 | functions/structure.rst 16 | functions/arguments.rst 17 | functions/param_parsing.rst 18 | functions/return_values.rst 19 | functions/error_handling.rst 20 | -------------------------------------------------------------------------------- /Book/TODO/ini.rst: -------------------------------------------------------------------------------- 1 | INI and configuration management 2 | ================================ 3 | 4 | A very big part of PHP behavior is driven by configuration parameters you 5 | should have heared about as ini settings. This chapter will teach you how to 6 | ask for some ini file parsing as well as how to cope with engine parameter be 7 | it into your extension or more globally in the engine itself. 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | .. 15 | ini/getting_config.rst 16 | ini/read_and_display.rst 17 | ini/adding_params.rst 18 | ini/ini_parser.rst 19 | -------------------------------------------------------------------------------- /Book/TODO/managing_memory.rst: -------------------------------------------------------------------------------- 1 | First step into code : Managing memory 2 | ====================================== 3 | 4 | Nobody can start programming inside PHP without knowing this layer. So we’ll 5 | start by introducing how to allocate/free/debug memory within PHP using the 6 | Zend Memory Manager layer 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | .. 14 | managing_memory/zendmm_intro.rst 15 | managing_memory/zendmm_config.rst 16 | managing_memory/useful_functions.rst 17 | managing_memory/persistent_mem.rst 18 | managing_memory/zendmm_internals.rst 19 | managing_memory/zendmm_in_phpland.rst 20 | managing_memory/phpext_examples.rst 21 | -------------------------------------------------------------------------------- /Book/TODO/resources.rst: -------------------------------------------------------------------------------- 1 | Resources 2 | ========= 3 | 4 | Resources may appear as a strange concept from PHP land. In fact, resources hold 5 | any data that can be held by other types, as they represent a sort of binding 6 | with an OS under structure, such as a file handle. Here, we'll detail how such 7 | type has been designed internally and how to deal with it. 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | .. 15 | resources/definition.rst 16 | resources/zend_list.rst 17 | resources/basic_api.rst 18 | resources/persistence.rst 19 | -------------------------------------------------------------------------------- /Book/TODO/sapis.rst: -------------------------------------------------------------------------------- 1 | Server Application Programming Interfaces : SAPI 2 | ================================================ 3 | 4 | When you want to ask PHP something, you need to contact it. This sound silly, 5 | but there are different ways of contacting PHP, different entry points. These 6 | are called SAPIs. This chapter will help you understand how they work, what 7 | their responsabilities are and finally, how to create your own, should you 8 | need it. 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | .. 16 | sapis/entry_point.rst 17 | sapis/list.rst 18 | sapis/structures.rst 19 | sapis/api.rst 20 | sapis/output_buffering.rst 21 | sapis/creation.rst 22 | -------------------------------------------------------------------------------- /Book/TODO/streams.rst: -------------------------------------------------------------------------------- 1 | Network streams 2 | =============== 3 | 4 | As far as you want to play with the network, you need OS specific calls such as 5 | sockets. PHP internals has abstracted all network calls in a muti-OS compatible 6 | complex layer taht we will now detail for you. 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | -------------------------------------------------------------------------------- /Book/TODO/zend_engine.rst: -------------------------------------------------------------------------------- 1 | Zend Engine 2 | =========== 3 | 4 | The big part, Zend Engine is PHP's heart. It's composed of many different pieces 5 | , each one having a responsability. They all play together to make PHP alive. 6 | Here you'll mainly dive into the virtual machine, and learn how PHP 7 | understands its own code, how it executes it on the fly and how it makes a 8 | response to your request. 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | .. 16 | zend_engine/lexer.rst 17 | zend_engine/parser.rst 18 | zend_engine/compiler.rst 19 | zend_engine/vm.rst 20 | zend_engine/vm_details.rst 21 | zend_engine/function_calls.rst 22 | -------------------------------------------------------------------------------- /Book/_static/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Book/_static/style.css: -------------------------------------------------------------------------------- 1 | div.content { 2 | font-size: 1em; 3 | } 4 | 5 | body { 6 | min-width: 45em; 7 | max-width: 60em; 8 | } -------------------------------------------------------------------------------- /Book/_templates/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Book/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% set css_files = css_files + ["_static/style.css"] %} 3 | {% block footer %} 4 | 13 | 22 | {% endblock %} -------------------------------------------------------------------------------- /Book/build_system.rst: -------------------------------------------------------------------------------- 1 | PHP yapı sistemini kullanmak 2 | ============================ 3 | 4 | Bu bölümde PHP yapı sistemini kullanarak PHP eklentilerini ve PHP'yi nasıl derleyeceğinizi anlatacağız. 5 | Henüz autoconf yapılandırma talimatları üzerinde durulmayacak, sadece araçların nasıl kullanılacağı 6 | anlatılacaktır. 7 | 8 | İçerik: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | build_system/building_php.rst 14 | build_system/building_extensions.rst 15 | -------------------------------------------------------------------------------- /Book/build_system/building_extensions.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: bash 2 | 3 | PHP eklentilerini yapılandırmak 4 | =============================== 5 | 6 | Artık PHP'nin kendisini nasıl derleyeceğinizi bildiğinize göre, ek uzantıları derlemeye devam edeceğiz. Derleme 7 | işleminin nasıl çalıştığını ve hangi farklı seçeneklerin mevcut olduğunu tartışacağız. 8 | 9 | Paylaşılan uzantıların yüklenmesi 10 | --------------------------------- 11 | 12 | Önceki bölümden zaten bildiğiniz gibi, PHP uzantıları ya PHP ikili olarak statik olarak oluşturulabilir ya da 13 | paylaşılan bir nesneye derlenebilir (``.so``). Statik bağlantılama, paketlenmiş uzantıların çoğu için varsayılandır; 14 | oysa paylaşılan nesneler açıkça ``--enable-EXTNAME=shared`` veya ``--with-EXTNAME=shared`` anahtarları 15 | ``./configure``'a gönderilerek oluşturulabilir. 16 | 17 | Statik uzantılar her zaman kullanılabilir olsa da, paylaşılan uzantıların ``extension`` ya da ``zend_extension`` ini 18 | seçenekleri kullanılarak yüklenmesi gerekir. Her iki seçenek de ``.so`` dosyasına giden mutlak yolu veya 19 | ``extension_dir`` ayarına göre belirtilen bir yolu kullanır. (``zend_extension`` için göreceli(relative) yollar sadece 20 | PHP 5.5'ten itibaren kullanılabilir, öncesinde mutlak(absolute) yollar kullanmak zorundaydık.) 21 | 22 | Örnek olarak, bu ayar satırı kullanılarak derlenmiş bir PHP yapısını düşünün:: 23 | 24 | ~/php-src> ./configure --prefix=$HOME/myphp \ 25 | --enable-debug --enable-maintainer-zts \ 26 | --enable-opcache --with-gmp=shared 27 | 28 | Bu durumda hem opcache hem de GMP eklentileri ``modules/`` dizininde bulunan, paylaşılan nesnelere derlenir. Her 29 | ikisini de ``extension_dir``'i değiştirerek veya mutlak yolu parametre olarak vererek yükleyebilirsiniz: 30 | 31 | ~/php-src> sapi/cli/php -dzend_extension=`pwd`/modules/opcache.so \ 32 | -dextension=`pwd`/modules/gmp.so 33 | # or 34 | ~/php-src> sapi/cli/php -dextension_dir=`pwd`/modules \ 35 | -dzend_extension=opcache.so -dextension=gmp.so 36 | 37 | ``make install`` adımında, her ikisi de ``.so`` dosyaları, PHP kurulumunuzun ``php-config --extension-dir`` komutunu 38 | kullanarak bulabileceğiniz eklenti dizinine taşınır. Bu dizin, yukarıdaki derleme seçenekleri için 39 | ``/home/myuser/myphp/lib/php/extensions/no-debug-non-zts-MODULE_API`` olacaktır. Bu değer ``extension_dir`` ini 40 | seçeneğinin de varsayılanı olacaktır, bu nedenle açıkça belirtmeniz gerekmez ve uzantıları doğrudan yükleyebilirsiniz:: 41 | 42 | ~/myphp> bin/php -dzend_extension=opcache.so -dextension=gmp.so 43 | 44 | Bu aklımıza şu soruyu getirir: Hangi mekanizmayı kullanmalısın? Paylaşılan nesneler temel bir PHP ikili dosyasına sahip 45 | olmanıza ve php.ini üzerinden ek uzantılar yüklemenize izin verir. Dağıtımlar bunu, sade bir PHP paketi sağlayarak ve 46 | uzantıları ayrı paketler olarak dağıtarak kullanır. Öte yandan, kendi PHP ikili kodunuzu derliyorsanız, buna ihtiyaç 47 | duymazsınız, çünkü hangi uzantılara ihtiyacınız olduğunu zaten biliyorsunuz. 48 | 49 | Genel bir kural olarak, PHP'nin kendisi tarafından paketlenen uzantılar için statik bağlantı kullanacak ve diğer 50 | her şey için paylaşılan uzantıları kullanacaksınız. Bunun nedeni, gördüğünüz gibi, harici uzantıları paylaşılan 51 | nesneler olarak oluşturmanın daha kolay (veya en azından daha az müdahaleci) olmasıdır. Diğer bir avantaj ise, PHP'yi 52 | yeniden oluşturmadan eklentiyi güncelleyebilmenizdir. 53 | 54 | .. [#] Kitapta daha sonra "normal" bir uzantı ile Zend uzantısı arasındaki farktan bahsedeceğiz. Şimdilik, Zend 55 | uzantılarının daha "düşük seviye" (örneğin opcache veya xdebug) olduğunu bilmek ve Zend Engine'in kendi 56 | çalışmalarına bağlanmak yeterli gelecektir. 57 | 58 | PECL'den uzantı yükleme 59 | ----------------------- 60 | 61 | PECL_, *PHP Extension Community Library*, PHP için çok sayıda uzantı sunar. Uzantılar ana PHP dağıtımından 62 | kaldırıldığında, genellikle PECL'de var olmaya devam ederler. Benzer şekilde, şimdi PHP ile birlikte gelen birçok 63 | uzantı daha önce PECL uzantılarıydı. 64 | 65 | PHP derlemenizin konfigürasyon aşamasında ``--without-pear`` anahtarını belirtmediyseniz, ``make install`` PEAR'ın bir 66 | parçası olarak PECL'yi indirip yükleyecektir. ``pecl`` betiğini ``$PREFIX/bin`` dizininde bulabilirsiniz. Eklentileri 67 | yüklemek son derece basittir, ``pecl install EXTNAME``, Örneğin:: 68 | 69 | ~/myphp> bin/pecl install apcu-4.0.2 70 | 71 | Bu komut APCu_ uzantısını indirecek, derleyecek ve yükleyecektir. Sonuç, uzantı dizininizde bir ``apcu.so`` dosyası 72 | olacaktır; bu, ``extension=apcu.so`` ini 'seçeneğinin kullanılmasıyla yüklenebilir. 73 | 74 | ``pecl install``, son kullanıcılar için çok kullanışlı olsa da, eklenti geliştiricilerinin ilgisini çekmiyor. Aşağıda, 75 | uzantıları manuel olarak oluşturmanın iki yolunu açıklayacağız: Ya ana PHP kaynak ağacına alarak (bu statik bağlantıya 76 | izin verir) ya da harici bir derleme yaparak (yalnızca paylaşılan). 77 | 78 | .. _PECL: http://pecl.php.net 79 | .. _APCu: http://pecl.php.net/package/APCu 80 | 81 | PHP kaynak ağacına uzantı ekleme 82 | -------------------------------- 83 | 84 | Üçüncü taraf bir uzantı ile PHP ile birlikte gelen bir uzantı arasında temel bir fark yoktur. Böylece harici bir 85 | eklentiyi PHP kaynak ağacına kopyalayarak ve daha sonra normal derleme prosedürünü kullanarak oluşturabilirsiniz. Bunun 86 | örneğini APCu'yu kullanarak göstereceğiz. 87 | 88 | Öncelikle, uzantının kaynak kodunu PHP kaynak ağacınızın ``ext/EXTNAME`` dizinine yerleştirmeniz gerekir. Uzantı git 89 | üzerinde mevcutsa, bu, depoyu ``ext/`` içinden klonlamak kadar basittir:: 90 | 91 | ~/php-src/ext> git clone https://github.com/krakjoe/apcu.git 92 | 93 | Alternatif olarak, bir kaynak tarball'ı indirebilir ve çıkarabilirsiniz:: 94 | 95 | /tmp> wget http://pecl.php.net/get/apcu-4.0.2.tgz 96 | /tmp> tar xzf apcu-4.0.2.tgz 97 | /tmp> mkdir ~/php-src/ext/apcu 98 | /tmp> cp -r apcu-4.0.2/. ~/php-src/ext/apcu 99 | 100 | Uzantı, autoconf tarafından kullanılacak uzantıya özgü derleme talimatlarını belirten bir ``config.m4`` dosyası 101 | içerecektir. Onları ``./configure`` betiğine dahil etmek için yeniden ``./buildconf``'u çalıştırmanız gerekir. 102 | Konfigürasyon dosyasının gerçekten yenilenmesini sağlamak için, önceden silmeniz önerilir:: 103 | 104 | ~/php-src> rm configure && ./buildconf --force 105 | 106 | Şimdi mevcut yapılandırmanıza APCu eklemek için ``./config.nice`` komut dosyasını kullanabilir veya tamamen yeni bir 107 | yapılandırma satırıyla başlayabilirsiniz:: 108 | 109 | ~/php-src> ./config.nice --enable-apcu 110 | # or 111 | ~/php-src> ./configure --enable-apcu # --other-options 112 | 113 | Sonunda asıl yapıyı oluşturmak için ``make -jN`` komutunu çalıştırın. ``--enable-apcu=shared`` anahtarını 114 | kullanmadığımız için, uzantı statik olarak PHP ikilisi ile bağlantılıdır, yani kullanımı için ek bir işlem yapmanıza 115 | gerek yoktur. Ortaya çıkan ikili dosyaları yüklemek için ``make install`` komutunu da kullanabilirsiniz. 116 | 117 | ``phpize`` kullanarak eklenti oluşturma 118 | --------------------------------------- 119 | 120 | Ayrıca :ref:`building_php` bölümünde daha önce bahsedilen ``phpize`` komut dosyasını kullanarak PHP'den ayrı uzantılar 121 | oluşturmak da mümkündür. 122 | 123 | ``phpize``, PHP için kullanılan ``./buildconf`` betiği ile benzer bir rol oynar: İlk olarak, PHP yapılandırma 124 | sistem dosyalarınızı ``$PREFIX/lib/php/build`` dizininden kopyalayarak eklentinize dahil edecektir. Bu dosyalar 125 | arasında ``acinclude.m4`` (PHP'nin M4 makroları), ``phpize.m4`` (``configure.in`` olarak değiştirilecek ve ana derleme 126 | talimatlarını içeren dosya) ve ``run-tests.php`` gibi dosyalar bulunur. 127 | 128 | Ardından ``phpize``, uzantı yapısını özelleştirmek için kullanılan bir ``./configure`` dosyası oluşturmak için 129 | autoconf'u çağırır. Burada “--enable-apcu” komutunu iletmek gerekli değildir. Bunun yerine `` --with-php-config``, 130 | ve `` php-config`` anahtarlarını scriptinizin yolunu belirtmek için kullanmalısınız. 131 | 132 | /tmp/apcu-4.0.2> ~/myphp/bin/phpize 133 | Configuring for: 134 | PHP Api Version: 20121113 135 | Zend Module Api No: 20121113 136 | Zend Extension Api No: 220121113 137 | 138 | /tmp/apcu-4.0.2> ./configure --with-php-config=$HOME/myphp/bin/php-config 139 | /tmp/apcu-4.0.2> make -jN && make install 140 | 141 | Eklentileri oluştururken, daima ``--with-php-config`` anahtarını belirtmelisiniz (PHP'nin tek bir global kurulumuna 142 | sahip değilseniz), aksi takdirde ``./configure`` hangi PHP sürümünün ve bayraklarının oluşturulacağını doğru bir 143 | şekilde belirleyemez. ``php-config`` betiğini belirtmek aynı zamanda ``make install``'ın oluşturmuş olduğu ``.so`` 144 | dosyasını (``/`` dizini içinde bulabileceğiniz) doğru eklenti dizinine taşımasını sağlar. ``run-tests.php`` dosyası 145 | ``phpize`` aşamasında da kopyalandığından, uzantı testlerini ``make test`` (veya testleri çalıştırmak için açık bir 146 | çağrı) kullanarak çalıştırabilirsiniz. 147 | 148 | Derlenmiş nesnelerin kaldırılması için ``make clean`` komutu da mevcuttur ve artımlı derleme bir değişiklikten sonra 149 | başarısız olursa, uzantının tamamını yeniden oluşturmaya zorlamanıza izin verir. Ek olarak phpize ``phpize --clean`` 150 | komutu üzerinden bir temizlik seçeneği sunar. Bu, ``phpize`` ile içeri alınan tüm dosyaları ve ``/configure`` betiği 151 | ile oluşturulan dosyaları kaldıracaktır. 152 | 153 | Uzantılarla ilgili bilgileri görüntüleme 154 | ---------------------------------------- 155 | 156 | PHP CLI ikilisi(binary), uzantılar hakkında bilgi görüntülemek için çeşitli seçenekler sunar. Yüklü tüm uzantıları 157 | listeleyecek olan ``-m`` parametresini zaten biliyorsunuz. Bunu aynı zamanda bir uzantının doğru yüklendiğini 158 | doğrulamak için de kullanabilirsiniz:: 159 | 160 | ~/myphp/bin> ./php -dextension=apcu.so -m | grep apcu 161 | apcu 162 | 163 | Reflection işlevini ortaya çıkaran ``--r`` ile başlayan birkaç anahtar daha var. Örneğin, bir uzantı yapılandırmasını 164 | görüntülemek için ``--ri`` kullanabilirsiniz:: 165 | 166 | ~/myphp/bin> ./php -dextension=apcu.so --ri apcu 167 | apcu 168 | 169 | APCu Support => disabled 170 | Version => 4.0.2 171 | APCu Debugging => Disabled 172 | MMAP Support => Enabled 173 | MMAP File Mask => 174 | Serialization Support => broken 175 | Revision => $Revision: 328290 $ 176 | Build Date => Jan 1 2014 16:40:00 177 | 178 | Directive => Local Value => Master Value 179 | apc.enabled => On => On 180 | apc.shm_segments => 1 => 1 181 | apc.shm_size => 32M => 32M 182 | apc.entries_hint => 4096 => 4096 183 | apc.gc_ttl => 3600 => 3600 184 | apc.ttl => 0 => 0 185 | # ... 186 | 187 | ``--re`` anahtarı, bir uzantı tarafından eklenen tüm dahili ayarları, sabitleri, işlevleri ve sınıfları listeler: 188 | 189 | .. code-block:: none 190 | 191 | ~/myphp/bin> ./php -dextension=apcu.so --re apcu 192 | Extension [ extension #27 apcu version 4.0.2 ] { 193 | - INI { 194 | Entry [ apc.enabled ] 195 | Current = '1' 196 | } 197 | Entry [ apc.shm_segments ] 198 | Current = '1' 199 | } 200 | # ... 201 | } 202 | 203 | - Constants [1] { 204 | Constant [ boolean APCU_APC_FULL_BC ] { 1 } 205 | } 206 | 207 | - Functions { 208 | Function [ function apcu_cache_info ] { 209 | 210 | - Parameters [2] { 211 | Parameter #0 [ $type ] 212 | Parameter #1 [ $limited ] 213 | } 214 | } 215 | # ... 216 | } 217 | } 218 | 219 | ``--re`` anahtarı yalnızca normal uzantılar için çalışır, Zend uzantıları için bunun yerine ``--rz`` kullanılır. Bunu 220 | opcache'de deneyebilirsiniz:: 221 | 222 | ~/myphp/bin> ./php -dzend_extension=opcache.so --rz "Zend OPcache" 223 | Zend Extension [ Zend OPcache 7.0.3-dev Copyright (c) 1999-2013 by Zend Technologies ] 224 | 225 | Gördüğünüz gibi, bu herhangi bir faydalı bilgi göstermiyor. Bunun nedeni, opcache’in hem normal bir uzantıyı hem de 226 | Zend uzantısını kaydetmesidir; burada, önceki tüm ini ayarları, sabitleri ve işlevleri içerir. Yani bu özel durumda 227 | hala ``--re`` kullanmanız gerekir. Diğer Zend uzantıları, bilgilerini ``--rz`` yoluyla da sağlar. 228 | 229 | .. 230 | nikic: Commented out for now. building_php.rst already mentions ABI incompatibility for zts / debug / api version. 231 | This has more detail regarding the 3 different API numbers, but it doesn't really become clear what they mean, and 232 | I don't know that either (it seems like we just have too many and they should be reduced to just PHP Api No and 233 | Zend Api No.) 234 | 235 | Extensions API compatibility 236 | **************************** 237 | 238 | Extensions are very sensitive to 5 major factors. If they dont fit, the extension wont load into PHP and will be useless : 239 | 240 | * PHP Api Version 241 | * Zend Module Api No 242 | * Zend Extension Api No 243 | * Debug mode 244 | * Thread safety 245 | 246 | The *phpize* tool recall you some of those informations. 247 | So if you have built a PHP with debug mode, and try to make it load and use an extension which's been built without 248 | debug mode, it simply wont work. Same for the other checks. 249 | 250 | *PHP Api Version* is the number of the version of the internal API. *Zend Module Api No* and *Zend Extension Api No* 251 | are respectively about PHP extensions and Zend extensions API. 252 | 253 | Those numbers are later passed as C macros to the extension beeing built, so that it can itself checks against those 254 | parameters and take different code paths based on C preprocessor ``#ifdef``\s As those numbers are passed to the 255 | extension code as macros, they are written in the extension structure, so that anytime you try to load this extension in 256 | a PHP binary, they will be checked against the PHP binary's own numbers. 257 | If they mismatch, then the extension will not load, and an error message will be displayed. 258 | 259 | If we look at the extension C structure, it looks like this:: 260 | 261 | zend_module_entry foo_module_entry = { 262 | STANDARD_MODULE_HEADER, 263 | "foo", 264 | foo_functions, 265 | PHP_MINIT(foo), 266 | PHP_MSHUTDOWN(foo), 267 | NULL, 268 | NULL, 269 | PHP_MINFO(foo), 270 | PHP_FOO_VERSION, 271 | STANDARD_MODULE_PROPERTIES 272 | }; 273 | 274 | What is interesting for us so far, is the ``STANDARD_MODULE_HEADER`` macro. If we expand it, we can see:: 275 | 276 | #define STANDARD_MODULE_HEADER_EX sizeof(zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS 277 | #define STANDARD_MODULE_HEADER STANDARD_MODULE_HEADER_EX, NULL, NULL 278 | 279 | Notice how ``ZEND_MODULE_API_NO``, ``ZEND_DEBUG``, ``USING_ZTS`` are used. 280 | 281 | 282 | If you look at the default directory for PHP extensions, it should look like ``no-debug-non-zts-20090626``. As you'd 283 | have guessed, this directory is made of distinct parts joined together : debug mode, followed by thread safety 284 | information, followed by the Zend Module Api No. 285 | So by default, PHP tries to help you navigating with extensions. 286 | 287 | .. note:: 288 | 289 | Usually, when you become an internal developper or an extension developper, you will usually have to play with the debug parameter, and if you have to deal with the Windows platform, threads will show up as well. You can end with compiling the same extension several times against several cases of those parameters. 290 | 291 | Remember that every new major/minor version of PHP change parameters such as the PHP Api Version, that's why you need to recompile extensions against a newer PHP version. 292 | 293 | .. code-block:: none 294 | 295 | > /path/to/php54/bin/phpize -v 296 | Configuring for: 297 | PHP Api Version: 20100412 298 | Zend Module Api No: 20100525 299 | Zend Extension Api No: 220100525 300 | 301 | > /path/to/php55/bin/phpize -v 302 | Configuring for: 303 | PHP Api Version: 20121113 304 | Zend Module Api No: 20121212 305 | Zend Extension Api No: 220121212 306 | 307 | > /path/to/php53/bin/phpize -v 308 | Configuring for: 309 | PHP Api Version: 20090626 310 | Zend Module Api No: 20090626 311 | Zend Extension Api No: 220090626 312 | 313 | .. note:: 314 | 315 | *Zend Module Api No* is itself built with a date using the *year.month.day* format. This is the date of the day the API changed and was tagged. 316 | *Zend Extension Api No* is the Zend version followed by *Zend Module Api No*. 317 | -------------------------------------------------------------------------------- /Book/classes_objects.rst: -------------------------------------------------------------------------------- 1 | Sınıflar ve nesneler 2 | ==================== 3 | 4 | Son yıllarda PHP, her geçen gün prosedürel bir dilden nesneye dayalı bir dile dönüşüyor. Temeller hala prosedürel 5 | nitelikte olmasına rağmen (özellikle de standart kütüphanenin büyük bölümleri) günümüzdeki kütüphaneler sınıflar ve 6 | nesne yönelimli geliştirilmektedir. Bu bölüm, PHP'nin nesne yönlü sisteminin oldukça karmaşık iç yapılarını 7 | kapsamaktadır. 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | classes_objects/simple_classes.rst 15 | classes_objects/custom_object_storage.rst 16 | classes_objects/implementing_typed_arrays.rst 17 | classes_objects/object_handlers.rst 18 | classes_objects/iterators.rst 19 | classes_objects/serialization.rst 20 | classes_objects/magic_interfaces_comparable.rst 21 | classes_objects/internal_structures_and_implementation.rst 22 | 23 | .. todo:: 24 | * __construct is not always called 25 | * verify that ctors don't segfault or leak on manual call -------------------------------------------------------------------------------- /Book/classes_objects/internal_structures_and_implementation.rst: -------------------------------------------------------------------------------- 1 | İç yapılar ve implementasyon 2 | ====================================== 3 | 4 | In this (last) section on object orientation in PHP we'll have a look at some of the internal structures that were 5 | previously only mentioned in passing. In particular we'll more thoroughly the default object structure and the object 6 | store. 7 | 8 | Object properties 9 | ----------------- 10 | 11 | The probably by far most complicated part of PHP's object orientation system is the handling of object properties. In 12 | the following we'll take a look at some of its parts in more detail. 13 | 14 | Property storage 15 | ~~~~~~~~~~~~~~~~ 16 | 17 | In PHP object properties can be declared, but don't have to. How can one efficiently handle such a situation? To find 18 | out let's recall the standard ``zend_object`` structure:: 19 | 20 | typedef struct _zend_object { 21 | zend_class_entry *ce; 22 | HashTable *properties; 23 | zval **properties_table; 24 | HashTable *guards; 25 | } zend_object; 26 | 27 | This structure contains two fields for storing properties: The ``properties`` hash table and the ``properties_table`` 28 | array of ``zval`` pointers. Two separate fields are used to best handle both declared and dynamic properties: For the 29 | latter, i.e. properties that have not been declared in the class, there is no way around using the ``properties`` 30 | hash table (which uses a simple property name => value mapping). 31 | 32 | For declared properties on the other hand storing them in a hashtable would be overly wasteful: PHP's hash tables 33 | have a very high per-element overhead (of nearly one hundred bytes), but the only thing that really needs to be stored 34 | is a ``zval`` pointer for the value. For this reason PHP employs a small trick: The properties are stored in a normal 35 | C array and accessed using their offset. The offset for each property name is stored in a (global) hashtable in the 36 | class entry. Thus the property lookup happens with one additional level of indirection, i.e. rather than directly 37 | fetching the property value, first the property offset is fetched and that offset is then used to fetch the actual 38 | value. 39 | 40 | Property information (including the storage offset) is stored in ``class_entry->properties_info``. This hash table 41 | is a map of property names to ``zend_property_info`` structs:: 42 | 43 | typedef struct _zend_property_info { 44 | zend_uint flags; 45 | const char *name; 46 | int name_length; 47 | ulong h; /* hash of name */ 48 | int offset; /* storage offset */ 49 | const char *doc_comment; 50 | int doc_comment_len; 51 | zend_class_entry *ce; /* CE of declaring class */ 52 | } zend_property_info; 53 | 54 | One remaining question is what happens when both types of properties exist. In this case both structures will be used 55 | simultaneously: All properties will be written into the ``properties`` hashtable, but ``properties_table`` will still 56 | contain pointers to them. Note though that if both are used the properties table holds ``zval**`` values rather than 57 | ``zval*`` values. 58 | 59 | Sometimes PHP needs the properties as a hashtable even if they are all declared, e.g. when the ``get_properties`` 60 | handler is used. In this case PHP also switches to using ``properties`` (or rather the hybrid approach described above). 61 | This is done using the ``rebuild_object_properties`` function:: 62 | 63 | ZEND_API HashTable *zend_std_get_properties(zval *object TSRMLS_DC) 64 | { 65 | zend_object *zobj; 66 | zobj = Z_OBJ_P(object); 67 | if (!zobj->properties) { 68 | rebuild_object_properties(zobj); 69 | } 70 | return zobj->properties; 71 | } 72 | 73 | Property name mangling 74 | ~~~~~~~~~~~~~~~~~~~~~~ 75 | 76 | Consider the following code snippet: 77 | 78 | .. code-block:: php 79 | 80 | class A { 81 | private $prop = 'A'; 82 | } 83 | 84 | class B extends A { 85 | private $prop = 'B'; 86 | } 87 | 88 | class C extends B { 89 | protected $prop = 'C'; 90 | } 91 | 92 | var_dump(new C); 93 | 94 | // Output: 95 | object(C)#1 (3) { 96 | ["prop":protected]=> 97 | string(1) "C" 98 | ["prop":"B":private]=> 99 | string(1) "B" 100 | ["prop":"A":private]=> 101 | string(1) "A" 102 | } 103 | 104 | In the above example you can see the "same" property ``$prop`` being defined three times: Once as a private property of 105 | ``A``, once as a private property of ``B`` and once as a protected property of ``C``. Even though these three properties 106 | have the same name they are still distinct properties and require separate storage. 107 | 108 | In order to support this situation PHP "mangles" the property name by including the type of the property and the 109 | defining class: 110 | 111 | .. code-block:: none 112 | 113 | class Foo { private $prop; } => "\0Foo\0prop" 114 | class Bar { private $prop; } => "\0Bar\0prop" 115 | class Rab { protected $prop; } => "\0*\0prop" 116 | class Oof { public $prop; } => "prop" 117 | 118 | As you can see public properties have "normal" names, protected ones get a ``\0*\0`` prefix (where ``\0`` are NUL bytes) 119 | and private ones start with ``\0ClassName\0``. 120 | 121 | Most of the time PHP does a good job hiding the mangled names from userland. You only get to see them in some rare 122 | cases, e.g. if you cast an object to array or look at serialization output. Internally you usually don't need to care 123 | about mangled names either, e.g. when using the ``zend_declare_property`` APIs the mangling is automatically done for 124 | you. 125 | 126 | The only places where you have to look out for mangled names is if you access the ``property_info->name`` field or if 127 | you try to directly access the ``zobj->properties`` hash. In this cases you can use the 128 | ``zend_(un)mangle_property_name`` APIs:: 129 | 130 | // Unmangling 131 | const char *class_name, *property_name; 132 | int property_name_len; 133 | 134 | if (zend_unmangle_property_name_ex( 135 | mangled_property_name, mangled_property_name_len, 136 | &class_name, &property_name, &property_name_len 137 | ) == SUCCESS) { 138 | // ... 139 | } 140 | 141 | // Mangling 142 | char *mangled_property_name; 143 | int mangled_property_name_len; 144 | 145 | zend_mangle_property_name( 146 | &mangled_property_name, &mangled_property_name_len, 147 | class_name, class_name_len, property_name, property_name_len, 148 | should_do_persistent_alloc ? 1 : 0 149 | ); 150 | 151 | Property recursion guards 152 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 153 | 154 | The last member in ``zend_object`` is the ``HashTable *guards`` field. To find out what it is used for, consider what 155 | happens in the following code using magic ``__set`` properties: 156 | 157 | .. code-block:: php 158 | 159 | class Foo { 160 | public function __set($name, $value) { 161 | $this->$name = $value; 162 | } 163 | } 164 | 165 | $foo = new Foo; 166 | $foo->bar = 'baz'; 167 | var_dump($foo->bar); 168 | 169 | The ``$foo->bar = 'baz'`` assignment in the script will call ``$foo->__set('bar', 'baz')`` as the ``$bar`` property is 170 | not defined. The ``$this->$name = $value`` line in the method body in this case would become ``$foo->bar = 'baz'``. 171 | Once again ``$bar`` is an undefined property. So, does that mean that the ``__set`` method will be (recursively) called 172 | again? 173 | 174 | That's not what happens. Rather PHP sees that it is already within ``__set`` and does *not* do a recursive call. Instead 175 | it actually creates the new ``$bar`` property. In order to implement this behavior PHP uses recursion guards which 176 | remember whether PHP is already in ``__set`` etc for a certain property. These guards are stored in the ``guards`` hash 177 | table, which maps property names to ``zend_guard`` structures:: 178 | 179 | typedef struct _zend_guard { 180 | zend_bool in_get; 181 | zend_bool in_set; 182 | zend_bool in_unset; 183 | zend_bool in_isset; 184 | zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */ 185 | } zend_guard; 186 | 187 | Object store 188 | ------------ 189 | 190 | We already made a lot of use of the object store, so let's have a closer look at it now:: 191 | 192 | typedef struct _zend_objects_store { 193 | zend_object_store_bucket *object_buckets; 194 | zend_uint top; 195 | zend_uint size; 196 | int free_list_head; 197 | } zend_objects_store; 198 | 199 | The object store is basically a dynamically resized array of ``object_buckets``. ``size`` specifies the size of the 200 | allocation, whereas ``top`` is the next object handle to be used. Handles are counted starting from 1, to ensure that 201 | all handles are "truthy". Thus if ``top == 1`` the next object will get ``handle = 1``, but will be put at position 202 | ``object_buckets[0]``. 203 | 204 | The ``free_list_head`` is the head of a linked list of unused buckets. Whenever an object is destroyed it leaves behind 205 | an unused bucket, which is then put in this list. If a new object is created and such a bucket exists (i.e. 206 | ``free_list_head`` is not ``-1``), then this bucket is used instead of the ``top`` one. 207 | 208 | To see how this linked list is maintained have a look at the ``zend_object_store_bucket`` structure:: 209 | 210 | typedef struct _zend_object_store_bucket { 211 | zend_bool destructor_called; 212 | zend_bool valid; 213 | zend_uchar apply_count; 214 | union _store_bucket { 215 | struct _store_object { 216 | void *object; 217 | zend_objects_store_dtor_t dtor; 218 | zend_objects_free_object_storage_t free_storage; 219 | zend_objects_store_clone_t clone; 220 | const zend_object_handlers *handlers; 221 | zend_uint refcount; 222 | gc_root_buffer *buffered; 223 | } obj; 224 | struct { 225 | int next; 226 | } free_list; 227 | } bucket; 228 | } zend_object_store_bucket; 229 | 230 | If the bucket is in use (i.e. stores an object), then the ``valid`` member will be 1. In this case the 231 | ``struct _store_object`` part of the union will be used. If the bucket is not used, then ``valid`` will be 0 and PHP 232 | will make use of ``free_list.next``. 233 | 234 | This reclaiming of unused object handles can be shown with a small script: 235 | 236 | .. code-block:: php 237 | 238 | var_dump($a = new stdClass); // object(stdClass)#1 (0) {} 239 | var_dump($b = new stdClass); // object(stdClass)#2 (0) {} 240 | var_dump($c = new stdClass); // object(stdClass)#3 (0) {} 241 | 242 | unset($b); // free handle 2 243 | unset($a); // free handle 1 244 | 245 | var_dump($e = new stdClass); // object(stdClass)#1 (0) {} 246 | var_dump($f = new stdClass); // object(stdClass)#2 (0) {} 247 | 248 | As you can see the handles of ``$b`` and ``$a`` are reused in reverse order of destruction. 249 | 250 | Apart from ``valid`` the bucket structure also contains a ``destructor_called`` flag. This flag is needed for PHP's 251 | two-phase object destruction process: As already outlined previously PHP has distinct dtor (can run userland code, isn't 252 | always run) and free (must not run userland code, is always executed) phases. After the dtor handler has been called, 253 | the ``destructor_called`` flag is set to 1, so that the dtor is not run again when the object is freed. 254 | 255 | The ``apply_count`` member serves the same role as the ``nApplyCount`` member of ``HashTable``: It protects against 256 | infinite recursion. It is used via the macros ``Z_OBJ_UNPROTECT_RECURSION(zval_ptr)`` (leave recursion) and 257 | ``Z_OBJ_PROTECT_RECURSION(zval_ptr)`` (enter recursion). The latter will throw an error if the nesting level for an 258 | object is 3 or larger. Currently this protection mechanism is only used in the object comparison handler. 259 | 260 | The ``handlers`` member in the ``_store_object`` struct is also required for destruction. The reason for this is that 261 | the ``dtor`` handler only gets passed the stored object and its handle:: 262 | 263 | typedef void (*zend_objects_store_dtor_t)(void *object, zend_object_handle handle TSRMLS_DC); 264 | 265 | But in order to call ``__destruct`` PHP needs a zval. Thus it creates a temporary zval using the passed object handle 266 | and the object handlers stored in ``bucket.obj.handlers``. The issue is that this member can only be set if the object 267 | is destructed through ``zval_ptr_dtor`` or some other method where the zval (and as such the object handlers) is known. 268 | 269 | If on the other hand the object is destroyed during shutdown (using ``zend_objects_store_call_destructors``) the zval 270 | is *not* known. In this case ``bucket.obj.handlers`` will be ``NULL`` and PHP falls back to the default object handlers. 271 | Thus it can sometimes happen that overloaded object behavior is not available in ``__destruct``. An example: 272 | 273 | .. code-block:: php 274 | 275 | class DLL extends SplDoublyLinkedList { 276 | public function __destruct() { 277 | var_dump($this); 278 | } 279 | } 280 | 281 | $dll = new DLL; 282 | $dll->push(1); 283 | $dll->push(2); 284 | $dll->push(3); 285 | 286 | var_dump($dll); 287 | 288 | set_error_handler(function() use ($dll) {}); 289 | 290 | This code snippet adds a ``__destruct`` method to ``SplDoublyLinkedList`` and then forces the destructor to be called 291 | during shutdown by binding it to the error handler (the error handler is one of the last things that is freed during 292 | shutdown.) This will produce the following output: 293 | 294 | .. code-block:: none 295 | 296 | object(DLL)#1 (2) { 297 | ["flags":"SplDoublyLinkedList":private]=> 298 | int(0) 299 | ["dllist":"SplDoublyLinkedList":private]=> 300 | array(3) { 301 | [0]=> 302 | int(1) 303 | [1]=> 304 | int(2) 305 | [2]=> 306 | int(3) 307 | } 308 | } 309 | object(DLL)#1 (0) { 310 | } 311 | 312 | For the ``var_dump`` outside the destructor ``get_debug_info`` is invoked and you get meaningful debugging output. 313 | Inside the destructor PHP uses the default object handlers and as such you don't get anything apart from the class 314 | name. The same also applies to other handlers, e.g. things like cloning, comparison, etc will not work properly. 315 | 316 | This concludes the chapter on object orientation. You should now have a good understanding of how the object orientation 317 | system in PHP works and how extensions can make use of it. -------------------------------------------------------------------------------- /Book/classes_objects/iterators.rst: -------------------------------------------------------------------------------- 1 | Yineleyiciler 2 | ============= 3 | 4 | In the last section we implemented a few object handlers to improve integration of typed arrays into the language. One 5 | aspect is still missing though: Iteration. In this section we'll look at how iterators are implemented internally and 6 | how we can make use of them. Once again typed arrays will serve as the example. 7 | 8 | The ``get_iterator`` handler 9 | ---------------------------- 10 | 11 | Internally iteration works very similar to the userland ``IteratorAggregate`` interface. The class has a 12 | ``get_iterator`` handler that returns a ``zend_object_iterator*``, which looks as follows:: 13 | 14 | struct _zend_object_iterator { 15 | void *data; 16 | zend_object_iterator_funcs *funcs; 17 | ulong index; /* private to fe_reset/fe_fetch opcodes */ 18 | }; 19 | 20 | The ``index`` member is used internally by the ``foreach`` implementation. It is incremented on each iteration and is 21 | used for the keys if you don't specify a custom key function. The ``funcs`` member contains handlers for the different 22 | iteration actions:: 23 | 24 | typedef struct _zend_object_iterator_funcs { 25 | /* release all resources associated with this iterator instance */ 26 | void (*dtor)(zend_object_iterator *iter TSRMLS_DC); 27 | 28 | /* check for end of iteration (FAILURE or SUCCESS if data is valid) */ 29 | int (*valid)(zend_object_iterator *iter TSRMLS_DC); 30 | 31 | /* fetch the item data for the current element */ 32 | void (*get_current_data)(zend_object_iterator *iter, zval ***data TSRMLS_DC); 33 | 34 | /* fetch the key for the current element (optional, may be NULL) */ 35 | void (*get_current_key)(zend_object_iterator *iter, zval *key TSRMLS_DC); 36 | 37 | /* step forwards to next element */ 38 | void (*move_forward)(zend_object_iterator *iter TSRMLS_DC); 39 | 40 | /* rewind to start of data (optional, may be NULL) */ 41 | void (*rewind)(zend_object_iterator *iter TSRMLS_DC); 42 | 43 | /* invalidate current value/key (optional, may be NULL) */ 44 | void (*invalidate_current)(zend_object_iterator *iter TSRMLS_DC); 45 | } zend_object_iterator_funcs; 46 | 47 | The handlers are pretty similar to the ``Iterator`` interface, only with slightly different names. The only handler 48 | that has no correspondence in userland is ``invalidate_current``, which can be used to destroy the current key/value. 49 | The handler is largely unused though, in particular ``foreach`` won't even call it. 50 | 51 | The last member in the struct is ``data``, which can be used to carry around some custom data. Usually this one slot 52 | isn't enough though, so instead of the structure is extended, similarly to what you have already seen with 53 | ``zend_object``. 54 | 55 | In order to iterate typed arrays we'll have to store a few things: First of all, we need to hold a reference to the 56 | buffer view object (otherwise it may be destroyed during iteration). We can store this in the ``data`` member. 57 | Furthermore we should keep around the ``buffer_view_object`` so we don't have to refetch it on every handler call. 58 | Additionally we'll have to store the current iteration ``offset`` and the ``zval*`` of the current element (you'll see 59 | a bit later why we need to do this):: 60 | 61 | typedef struct _buffer_view_iterator { 62 | zend_object_iterator intern; 63 | buffer_view_object *view; 64 | size_t offset; 65 | zval *current; 66 | } buffer_view_iterator; 67 | 68 | Let's also declare a dummy ``zend_object_iterator_funcs`` structure so we have something to work on:: 69 | 70 | static zend_object_iterator_funcs buffer_view_iterator_funcs = { 71 | buffer_view_iterator_dtor, 72 | buffer_view_iterator_valid, 73 | buffer_view_iterator_get_current_data, 74 | buffer_view_iterator_get_current_key, 75 | buffer_view_iterator_move_forward, 76 | buffer_view_iterator_rewind 77 | }; 78 | 79 | Now we can implement the ``get_iterator`` handler. This handler receives the class entry, the object and whether the 80 | iteration is done by reference and returns a ``zend_object_iterator*``. All we have to do is allocate the iterator and 81 | set the respective members:: 82 | 83 | zend_object_iterator *buffer_view_get_iterator( 84 | zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC 85 | ) { 86 | buffer_view_iterator *iter; 87 | 88 | if (by_ref) { 89 | zend_throw_exception(NULL, "Cannot iterate buffer view by reference", 0 TSRMLS_CC); 90 | return NULL; 91 | } 92 | 93 | iter = emalloc(sizeof(buffer_view_iterator)); 94 | iter->intern.funcs = &buffer_view_iterator_funcs; 95 | 96 | iter->intern.data = object; 97 | Z_ADDREF_P(object); 98 | 99 | iter->view = zend_object_store_get_object(object TSRMLS_CC); 100 | iter->offset = 0; 101 | iter->current = NULL; 102 | 103 | return (zend_object_iterator *) iter; 104 | } 105 | 106 | Finally we have to adjust the macro for registering buffer view classes:: 107 | 108 | #define DEFINE_ARRAY_BUFFER_VIEW_CLASS(class_name, type) \ 109 | INIT_CLASS_ENTRY(tmp_ce, #class_name, array_buffer_view_functions); \ 110 | type##_array_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC); \ 111 | type##_array_ce->create_object = array_buffer_view_create_object; \ 112 | type##_array_ce->get_iterator = buffer_view_get_iterator; \ 113 | type##_array_ce->iterator_funcs.funcs = &buffer_view_iterator_funcs; \ 114 | zend_class_implements(type##_array_ce TSRMLS_CC, 2, \ 115 | zend_ce_arrayaccess, zend_ce_traversable); 116 | 117 | The new things are the assignment to the ``get_iterator`` and ``iterator_funcs.funcs`` as well as the implementation 118 | of the ``Traversable`` interface. 119 | 120 | Iterator functions 121 | ------------------ 122 | 123 | Now let's actually implement the ``buffer_view_iterator_funcs`` that we specified above:: 124 | 125 | static void buffer_view_iterator_dtor(zend_object_iterator *intern TSRMLS_DC) 126 | { 127 | buffer_view_iterator *iter = (buffer_view_iterator *) intern; 128 | 129 | if (iter->current) { 130 | zval_ptr_dtor(&iter->current); 131 | } 132 | 133 | zval_ptr_dtor((zval **) &intern->data); 134 | efree(iter); 135 | } 136 | 137 | static int buffer_view_iterator_valid(zend_object_iterator *intern TSRMLS_DC) 138 | { 139 | buffer_view_iterator *iter = (buffer_view_iterator *) intern; 140 | 141 | return iter->offset < iter->view->length ? SUCCESS : FAILURE; 142 | } 143 | 144 | static void buffer_view_iterator_get_current_data( 145 | zend_object_iterator *intern, zval ***data TSRMLS_DC 146 | ) { 147 | buffer_view_iterator *iter = (buffer_view_iterator *) intern; 148 | 149 | if (iter->current) { 150 | zval_ptr_dtor(&iter->current); 151 | } 152 | 153 | if (iter->offset < iter->view->length) { 154 | iter->current = buffer_view_offset_get(iter->view, iter->offset); 155 | *data = &iter->current; 156 | } else { 157 | *data = NULL; 158 | } 159 | } 160 | 161 | #if ZEND_MODULE_API_NO >= 20121212 162 | static void buffer_view_iterator_get_current_key( 163 | zend_object_iterator *intern, zval *key TSRMLS_DC 164 | ) { 165 | buffer_view_iterator *iter = (buffer_view_iterator *) intern; 166 | ZVAL_LONG(key, iter->offset); 167 | } 168 | #else 169 | static int buffer_view_iterator_get_current_key( 170 | zend_object_iterator *intern, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC 171 | ) { 172 | buffer_view_iterator *iter = (buffer_view_iterator *) intern; 173 | 174 | *int_key = (ulong) iter->offset; 175 | return HASH_KEY_IS_LONG; 176 | } 177 | #endif 178 | 179 | static void buffer_view_iterator_move_forward(zend_object_iterator *intern TSRMLS_DC) 180 | { 181 | buffer_view_iterator *iter = (buffer_view_iterator *) intern; 182 | 183 | iter->offset++; 184 | } 185 | 186 | static void buffer_view_iterator_rewind(zend_object_iterator *intern TSRMLS_DC) 187 | { 188 | buffer_view_iterator *iter = (buffer_view_iterator *) iter; 189 | 190 | iter->offset = 0; 191 | iter->current = NULL; 192 | } 193 | 194 | The functions should be rather straightforward, so only a few comments: 195 | 196 | ``get_current_data`` gets a ``zval*** data`` as the parameter and expects us to write a ``zval**`` into it using 197 | ``*data = ...``. The ``zval**`` is required because iteration can also happen by reference, in which case ``zval*`` 198 | won't suffice. The ``zval**`` is the reason why we have to store the current ``zval*`` in the iterator. 199 | 200 | How the ``get_current_key`` handler looks like depends on the PHP version: With PHP 5.5 you simply have to write the 201 | key into the passed ``key`` variable using one of the ``ZVAL_*`` macros. 202 | 203 | On older versions of PHP the ``get_current_key`` handler takes three parameters that can be set depending on which key 204 | type is returned. If you return ``HASH_KEY_NON_EXISTANT`` the resulting key will be ``null`` and you don't have to set 205 | any of them. For ``HASH_KEY_IS_LONG`` you set the ``int_key`` argument. For ``HASH_KEY_IS_STRING`` you have to set 206 | ``str_key`` and ``str_key_len``. Note that here ``str_key_len`` is the string length plus one (similar to how it is done 207 | in the ``zend_hash`` APIs). 208 | 209 | Honoring inheritance 210 | -------------------- 211 | 212 | Once again we need to consider what happens when the user extends the class and wants to change the iteration behavior. 213 | Right now he would have to reimplement the iteration mechanism manually, because the individual iteration handlers are 214 | not exposed to userland (only through foreach). 215 | 216 | As already with the object handlers we'll solve this by also implementing the normal ``Iterator`` interface. This time 217 | we won't need special handling to ensure that PHP actually calls the overridden methods: PHP will automatically use the 218 | fast internal handlers when the class is used directly, but will use the ``Iterator`` methods if the class is extended. 219 | 220 | In order to implement the ``Iterator`` methods we have to add a new ``size_t current_offset`` member to 221 | ``buffer_view_object``, which stores the current offset for the iteration methods (and is completely separate from the 222 | iteration state used by ``get_iterator``-style iterators). The methods itself are to the most part just argument 223 | checking boilerplate:: 224 | 225 | PHP_FUNCTION(array_buffer_view_rewind) 226 | { 227 | buffer_view_object *intern; 228 | 229 | if (zend_parse_parameters_none() == FAILURE) { 230 | return; 231 | } 232 | 233 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 234 | intern->current_offset = 0; 235 | } 236 | 237 | PHP_FUNCTION(array_buffer_view_next) 238 | { 239 | buffer_view_object *intern; 240 | 241 | if (zend_parse_parameters_none() == FAILURE) { 242 | return; 243 | } 244 | 245 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 246 | intern->current_offset++; 247 | } 248 | 249 | PHP_FUNCTION(array_buffer_view_valid) 250 | { 251 | buffer_view_object *intern; 252 | 253 | if (zend_parse_parameters_none() == FAILURE) { 254 | return; 255 | } 256 | 257 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 258 | RETURN_BOOL(intern->current_offset < intern->length); 259 | } 260 | 261 | PHP_FUNCTION(array_buffer_view_key) 262 | { 263 | buffer_view_object *intern; 264 | 265 | if (zend_parse_parameters_none() == FAILURE) { 266 | return; 267 | } 268 | 269 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 270 | RETURN_LONG((long) intern->current_offset); 271 | } 272 | 273 | PHP_FUNCTION(array_buffer_view_current) 274 | { 275 | buffer_view_object *intern; 276 | zval *value; 277 | 278 | if (zend_parse_parameters_none() == FAILURE) { 279 | return; 280 | } 281 | 282 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 283 | value = buffer_view_offset_get(intern, intern->current_offset); 284 | RETURN_ZVAL(value, 1, 1); 285 | } 286 | 287 | /* ... */ 288 | 289 | ZEND_BEGIN_ARG_INFO_EX(arginfo_buffer_view_void, 0, 0, 0) 290 | ZEND_END_ARG_INFO() 291 | 292 | /* ... */ 293 | 294 | PHP_ME_MAPPING(rewind, array_buffer_view_rewind, arginfo_buffer_view_void, ZEND_ACC_PUBLIC) 295 | PHP_ME_MAPPING(next, array_buffer_view_next, arginfo_buffer_view_void, ZEND_ACC_PUBLIC) 296 | PHP_ME_MAPPING(valid, array_buffer_view_valid, arginfo_buffer_view_void, ZEND_ACC_PUBLIC) 297 | PHP_ME_MAPPING(key, array_buffer_view_key, arginfo_buffer_view_void, ZEND_ACC_PUBLIC) 298 | PHP_ME_MAPPING(current, array_buffer_view_current, arginfo_buffer_view_void, ZEND_ACC_PUBLIC) 299 | 300 | Obviously we now should also implement ``Iterator`` rather than ``Traversable``:: 301 | 302 | #define DEFINE_ARRAY_BUFFER_VIEW_CLASS(class_name, type) \ 303 | INIT_CLASS_ENTRY(tmp_ce, #class_name, array_buffer_view_functions); \ 304 | type##_array_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC); \ 305 | type##_array_ce->create_object = array_buffer_view_create_object; \ 306 | type##_array_ce->get_iterator = buffer_view_get_iterator; \ 307 | type##_array_ce->iterator_funcs.funcs = &buffer_view_iterator_funcs; \ 308 | zend_class_implements(type##_array_ce TSRMLS_CC, 2, \ 309 | zend_ce_arrayaccess, zend_ce_iterator); 310 | 311 | One last consideration regarding this: In general it is always better to implement ``IteratorAggregate`` rather than 312 | ``Iterator``, because ``IteratorAggregate`` decouples the iterator state from the main object. This is obviously simply 313 | better design, but also allows things like independent nested iteration. I still chose to implement ``Iterator`` here, 314 | because aggregates have a higher implementational overhead (as they require a separate class that has to interact with 315 | an independent object). -------------------------------------------------------------------------------- /Book/classes_objects/magic_interfaces_comparable.rst: -------------------------------------------------------------------------------- 1 | Sihirli arayüzler - Karşılaştırılabilir 2 | ======================================= 3 | 4 | PHP'deki dahili arayüzler, kullanıcı eşdeğerlerine çok benzer. Tek önemli fark, iç arayüzlerin, arayüz uygulandığında 5 | yürütülen bir işleyiciyi belirtme ihtimaline sahip olmasıdır. Bu özellik, ek kısıtlamalar uygulamak veya işleyicileri 6 | değiştirmek gibi çeşitli amaçlar için kullanılabilir. Bunu, iç ``compare_objects`` işleyicisini kullanıcı alanına 7 | gösteren "büyülü" ``Comparable`` arayüzünü uygulamak için kullanacağız. 8 | 9 | Arayüzün kendisi aşağıdaki gibidir:: 10 | 11 | .. code-block:: php 12 | 13 | interface Comparable { 14 | static function compare($left, $right); 15 | } 16 | 17 | Öncelikle bu yeni arayüzü ``MINIT``'a kaydedelim:: 18 | 19 | zend_class_entry *comparable_ce; 20 | 21 | ZEND_BEGIN_ARG_INFO_EX(arginfo_comparable, 0, 0, 2) 22 | ZEND_ARG_INFO(0, obj1) 23 | ZEND_ARG_INFO(0, obj2) 24 | ZEND_END_ARG_INFO() 25 | 26 | const zend_function_entry comparable_functions[] = { 27 | ZEND_FENTRY( 28 | compare, NULL, arginfo_comparable, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_STATIC 29 | ) 30 | PHP_FE_END 31 | }; 32 | 33 | PHP_MINIT_FUNCTION(comparable) 34 | { 35 | zend_class_entry tmp_ce; 36 | INIT_CLASS_ENTRY(tmp_ce, "Comparable", comparable_functions); 37 | comparable_ce = zend_register_internal_interface(&tmp_ce TSRMLS_CC); 38 | 39 | return SUCCESS; 40 | } 41 | 42 | Bu durumda ``PHP_ABSTRACT_ME`` kullanamayacağımıza dikkat edin, çünkü statik soyut yöntemleri desteklemiyor. Bunun 43 | yerine, düşük seviyeli ``ZEND_FENTRY`` makrosunu kullanmalıyız. 44 | 45 | Sonrasında ``interface_gets_implemented`` işleyicisini uyguluyoruz:: 46 | 47 | static int implement_comparable(zend_class_entry *interface, zend_class_entry *ce TSRMLS_DC) 48 | { 49 | if (ce->create_object != NULL) { 50 | zend_error(E_ERROR, "Comparable interface can only be used on userland classes"); 51 | } 52 | 53 | ce->create_object = comparable_create_object_override; 54 | 55 | return SUCCESS; 56 | } 57 | 58 | // in MINIT 59 | comparable_ce->interface_gets_implemented = implement_comparable; 60 | 61 | Arayüz uygulandığında, ``implement_comparable`` işlevi çağrılacaktır. Bu fonksiyonda ``create_object`` işleyicisinin 62 | sınıflarını geçersiz kılıyoruz. İşleri kolaylaştırmak için, arayüzün sadece ``create_object`` ``NULL`` iken 63 | kullanılmasına izin veriyoruz ("normal" bir kullanıcı sınıfıdır). Eski ``create_object`` işleyicisini bir yerde 64 | yedekleyerek bu çalışma üzerinde farklı denemeler de yapabiliriz. 65 | 66 | Bizim ``create_object`` geçersiz kılmamızda, nesneyi her zamanki gibi oluştururuz ancak kendi işleyici yapımızı özel 67 | bir ``compare_objects`` işleyici ile atarız:: 68 | 69 | static zend_object_handlers comparable_handlers; 70 | 71 | static zend_object_value comparable_create_object_override(zend_class_entry *ce TSRMLS_DC) 72 | { 73 | zend_object *object; 74 | zend_object_value retval; 75 | 76 | retval = zend_objects_new(&object, ce TSRMLS_CC); 77 | object_properties_init(object, ce); 78 | 79 | retval.handlers = &comparable_handlers; 80 | 81 | return retval; 82 | } 83 | 84 | // In MINIT 85 | memcpy(&comparable_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 86 | comparable_handlers.compare_objects = comparable_compare_objects; 87 | 88 | Son olarak, özel karşılaştırma işleyicisini uygulamak zorundayız. Bu, ``zend_call_method_with_2_params`` makrosunu 89 | kullanarak, ``zend_interfaces.h`` içinde tanımlanan ``compare`` yöntemini çağırır. Bunun sonucunda akıllara, yöntemin 90 | hangi sınıfa çağrılması gerektiği sorusu gelir. Bu uygulama için ilk geçirilen nesneyi kullanacağız, ancak bu sadece 91 | isteğe bağlı bir seçimdir. Bu, ``$left < $right`` için ``$left`` sınıfının kullanılacağı, ancak 92 | ``$left > $right`` için ``$right`` sınıfının kullanıldığı anlamına gelir. (Çünkü PHP ``>`` işlemini ``<``işlemine 93 | dönüştürür.) 94 | 95 | :: 96 | 97 | #include "zend_interfaces.h" 98 | 99 | static int comparable_compare_objects(zval *obj1, zval *obj2 TSRMLS_DC) 100 | { 101 | zval *retval = NULL; 102 | int result; 103 | 104 | zend_call_method_with_2_params(NULL, Z_OBJCE_P(obj1), NULL, "compare", &retval, obj1, obj2); 105 | 106 | if (!retval || Z_TYPE_P(retval) == IS_NULL) { 107 | if (retval) { 108 | zval_ptr_dtor(&retval); 109 | } 110 | return zend_get_std_object_handlers()->compare_objects(obj1, obj2 TSRMLS_CC); 111 | } 112 | 113 | convert_to_long_ex(&retval); 114 | result = ZEND_NORMALIZE_BOOL(Z_LVAL_P(retval)); 115 | zval_ptr_dtor(&retval); 116 | 117 | return result; 118 | } 119 | 120 | Yukarıda kullanılan ``ZEND_NORMALIZE_BOOL`` makrosu, döndürülen tamsayıyı ``-1``, ``0`` ve ``1`` olarak normalleştirir. 121 | 122 | Ve hepsi bu. Şimdi yeni arayüzü deneyebiliriz (örnek mantıklı gelmiyorsa üzgünüm): 123 | 124 | .. code-block:: php 125 | 126 | class Point implements Comparable { 127 | protected $x, $y, $z; 128 | 129 | public function __construct($x, $y, $z) { 130 | $this->x = $x; $this->y = $y; $this->z = $z; 131 | } 132 | 133 | /* We assume a point is smaller/greater if all its components are smaller/greater */ 134 | public static function compare($p1, $p2) { 135 | if ($p1->x == $p2->x && $p1->y == $p2->y && $p1->z == $p2->z) { 136 | return 0; 137 | } 138 | 139 | if ($p1->x < $p2->x && $p1->y < $p2->y && $p1->z < $p2->z) { 140 | return -1; 141 | } 142 | 143 | if ($p1->x > $p2->x && $p1->y > $p2->y && $p1->z > $p2->z) { 144 | return 1; 145 | } 146 | 147 | // not comparable 148 | return 1; 149 | } 150 | } 151 | 152 | $p1 = new Point(1, 1, 1); 153 | $p2 = new Point(2, 2, 2); 154 | $p3 = new Point(1, 0, 2); 155 | 156 | var_dump($p1 < $p2, $p1 > $p2, $p1 == $p2); // true, false, false 157 | 158 | var_dump($p1 == $p1); // true 159 | 160 | var_dump($p1 < $p3, $p1 > $p3, $p1 == $p3); // false, false, false 161 | 162 | -------------------------------------------------------------------------------- /Book/classes_objects/serialization.rst: -------------------------------------------------------------------------------- 1 | Serileştirme 2 | ============= 3 | 4 | In this section we'll have a look at PHP's serialization format and the different mechanisms PHP provides to serialize 5 | object data. As usual we'll use the typed arrays implementation as an example. 6 | 7 | PHP's serialization format 8 | -------------------------- 9 | 10 | You probably already know how the output of ``serialize()`` roughly looks like: It has some kind of type specifier (like 11 | ``s`` or ``i``), followed by a colon, followed by the actual data, followed by a semicolon. As such the serialization 12 | format for the "simple" types looks as follows: 13 | 14 | .. code-block:: none 15 | 16 | NULL: N; 17 | true: b:1; 18 | false: b:0; 19 | 42: i:42; 20 | 21 | 42.3789: d:42.378900000000002; 22 | ^-- Precision controlled by serialize_precision ini setting (default 17) 23 | 24 | "foobar": s:6:"foobar"; 25 | ^-- strlen("foobar") 26 | 27 | resource: i:0; 28 | ^-- Resources can't really be serialized, so they just get the value int(0) 29 | 30 | For arrays a list of key-value pairs is contained in curly braces: 31 | 32 | .. code-block:: none 33 | 34 | [10, 11, 12]: a:3:{i:0;i:10;i:1;i:11;i:2;i:12;} 35 | ^-- count([10, 11, 12]) 36 | 37 | v-- key v-- value 38 | ["foo" => 4, "bar" => 2]: a:2:{s:3:"foo";i:4;s:3:"bar";i:2;} 39 | ^-- key ^-- value 40 | 41 | For objects there are two serialization mechanisms: The first one simply serializes the object properties just like it 42 | is done for arrays. This mechanism uses ``O`` as the type specifier. 43 | 44 | Consider the following class: 45 | 46 | .. code-block:: php 47 | 48 | class Test { 49 | public $public = 1; 50 | protected $protected = 2; 51 | private $private = 3; 52 | } 53 | 54 | This is serialized as follows: 55 | 56 | .. code-block:: none 57 | 58 | v-- strlen("Test") v-- property v-- value 59 | O:4:"Test":3:{s:6:"public";i:1;s:12:"\0*\0protected";i:2;s:13:"\0Test\0private";i:3;} 60 | ^-- property ^-- value ^-- property ^-- value 61 | 62 | The ``\0`` in the above serialization string are NUL bytes. As you can see private and protected members are serialized 63 | with rather peculiar names: Private properties are prefixed with ``\0ClassName\0`` and protected properties with 64 | ``\0*\0``. These names are the result of name mangling, which is something we'll cover in a later section. 65 | 66 | The second mechanism allows for custom serialization formats. It delegates the actual serialization to the ``serialize`` 67 | method of the ``Serializable`` interface and uses the ``C`` type specifier. For example consider this class: 68 | 69 | .. code-block:: php 70 | 71 | class Test2 implements Serializable { 72 | public function serialize() { 73 | return "foobar"; 74 | } 75 | public function unserialize($str) { 76 | // ... 77 | } 78 | } 79 | 80 | It will be serialized as follows: 81 | 82 | .. code-block:: none 83 | 84 | C:5:"Test2":6:{foobar} 85 | ^-- strlen("foobar") 86 | 87 | In this case PHP will just put the result of the ``Serializable::serialize()`` call inside the curly braces. 88 | 89 | Another feature of PHP's serialization format is that it will properly preserve references: 90 | 91 | .. code-block:: none 92 | 93 | $a = ["foo"]; 94 | $a[1] =& $a[0]; 95 | 96 | a:2:{i:0;s:3:"foo";i:1;R:2;} 97 | 98 | The important part here is the ``R:2;`` element. It means "reference to the second value". What is the second value? 99 | The whole array is the first value, the first index (``s:3:"foo"``) is the second value, so that's what is referenced. 100 | 101 | As objects in PHP exhibit a reference-like behavior ``serialize`` also makes sure that the same object occurring twice 102 | will really be the same object on unserialization: 103 | 104 | .. code-block:: none 105 | 106 | $o = new stdClass; 107 | $o->foo = $o; 108 | 109 | O:8:"stdClass":1:{s:3:"foo";r:1;} 110 | 111 | As you can see it works the same way as with references, just using the small ``r`` instead of ``R``. 112 | 113 | Serializing internal objects 114 | ---------------------------- 115 | 116 | As internal objects don't store their data in ordinary properties PHP's default serialization mechanism will not work. 117 | For example, if you try to serialize an ``ArrayBuffer`` all you'll get is this: 118 | 119 | .. code-block:: none 120 | 121 | O:11:"ArrayBuffer":0:{} 122 | 123 | Thus we'll have to write a custom handler for serialization. As mentioned above there are two ways in which objects can 124 | be serialized (``O`` and ``C``). I'll demonstrate how to use both, starting with the ``C`` format that uses the 125 | ``Serializable`` interface. For this method we'll create our own serialization format based on the primitives that are 126 | provided by ``serialize``. In order to do so we need to include two headers:: 127 | 128 | #include "ext/standard/php_var.h" 129 | #include "ext/standard/php_smart_str.h" 130 | 131 | The ``php_var.h`` header exports some serialization functions, the ``php_smart_str.h`` header contains PHPs 132 | ``smart_str`` API. This API provides a dynamically resized string structure, that allows us to easily create strings 133 | without concerning ourselves with allocation. 134 | 135 | Now let's see how the ``serialize`` method for an ``ArrayBuffer`` could look like:: 136 | 137 | PHP_METHOD(ArrayBuffer, serialize) 138 | { 139 | buffer_object *intern; 140 | smart_str buf = {0}; 141 | php_serialize_data_t var_hash; 142 | zval zv, *zv_ptr = &zv; 143 | 144 | if (zend_parse_parameters_none() == FAILURE) { 145 | return; 146 | } 147 | 148 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 149 | if (!intern->buffer) { 150 | return; 151 | } 152 | 153 | PHP_VAR_SERIALIZE_INIT(var_hash); 154 | 155 | INIT_PZVAL(zv_ptr); 156 | 157 | /* Serialize buffer as string */ 158 | ZVAL_STRINGL(zv_ptr, (char *) intern->buffer, (int) intern->length, 0); 159 | php_var_serialize(&buf, &zv_ptr, &var_hash TSRMLS_CC); 160 | 161 | /* Serialize properties as array */ 162 | Z_ARRVAL_P(zv_ptr) = zend_std_get_properties(getThis() TSRMLS_CC); 163 | Z_TYPE_P(zv_ptr) = IS_ARRAY; 164 | php_var_serialize(&buf, &zv_ptr, &var_hash TSRMLS_CC); 165 | 166 | PHP_VAR_SERIALIZE_DESTROY(var_hash); 167 | 168 | if (buf.c) { 169 | RETURN_STRINGL(buf.c, buf.len, 0); 170 | } 171 | } 172 | 173 | Apart from the usual boilerplate this method contains a few interesting elements: Firstly, we declared a 174 | ``php_serialize_data_t var_hash`` variable, which is initialized with ``PHP_VAR_SERIALIZE_INIT`` and destroyed with 175 | ``PHP_VAR_SERIALIZE_DESTROY``. This variable is really of type ``HashTable*`` and is used to remember the serialized 176 | values for the ``R``/``r`` reference preservation mechanism. 177 | 178 | Furthermore we create a smart string using ``smart_str buf = {0}``. The ``= {0}`` initializes all members of the struct 179 | with zero. This struct looks as follows:: 180 | 181 | typedef struct { 182 | char *c; 183 | size_t len; 184 | size_t a; 185 | } smart_str; 186 | 187 | ``c`` is the buffer of the string, ``len`` the currently used length and ``a`` the size of the current allocation (as 188 | this is smart string this doesn't necessarily match ``len``). 189 | 190 | The serialization itself happens by using a dummy zval (``zv_ptr``). We first write a value into it and then call 191 | ``php_var_serialize``. The first serialized value is the actual buffer (as a string), the second value are the 192 | properties (as an array). 193 | 194 | A bit more complicated is the ``unserialize`` method:: 195 | 196 | PHP_METHOD(ArrayBuffer, unserialize) 197 | { 198 | buffer_object *intern; 199 | char *str; 200 | int str_len; 201 | php_unserialize_data_t var_hash; 202 | const unsigned char *p, *max; 203 | zval zv, *zv_ptr = &zv; 204 | 205 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) { 206 | return; 207 | } 208 | 209 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 210 | 211 | if (intern->buffer) { 212 | zend_throw_exception( 213 | NULL, "Cannot call unserialize() on an already constructed object", 0 TSRMLS_CC 214 | ); 215 | return; 216 | } 217 | 218 | PHP_VAR_UNSERIALIZE_INIT(var_hash); 219 | 220 | p = (unsigned char *) str; 221 | max = (unsigned char *) str + str_len; 222 | 223 | INIT_ZVAL(zv); 224 | if (!php_var_unserialize(&zv_ptr, &p, max, &var_hash TSRMLS_CC) 225 | || Z_TYPE_P(zv_ptr) != IS_STRING || Z_STRLEN_P(zv_ptr) == 0) { 226 | zend_throw_exception(NULL, "Could not unserialize buffer", 0 TSRMLS_CC); 227 | goto exit; 228 | } 229 | 230 | intern->buffer = Z_STRVAL_P(zv_ptr); 231 | intern->length = Z_STRLEN_P(zv_ptr); 232 | 233 | INIT_ZVAL(zv); 234 | if (!php_var_unserialize(&zv_ptr, &p, max, &var_hash TSRMLS_CC) 235 | || Z_TYPE_P(zv_ptr) != IS_ARRAY) { 236 | zend_throw_exception(NULL, "Could not unserialize properties", 0 TSRMLS_CC); 237 | goto exit; 238 | } 239 | 240 | if (zend_hash_num_elements(Z_ARRVAL_P(zv_ptr)) != 0) { 241 | zend_hash_copy( 242 | zend_std_get_properties(getThis() TSRMLS_CC), Z_ARRVAL_P(zv_ptr), 243 | (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *) 244 | ); 245 | } 246 | 247 | exit: 248 | zval_dtor(zv_ptr); 249 | PHP_VAR_UNSERIALIZE_DESTROY(var_hash); 250 | } 251 | 252 | The ``unserialize`` method again declares a ``var_hash`` variable, this time of type ``php_unserialize_data_t``, 253 | initialized with ``PHP_VAR_UNSERIALIZE_INIT`` and destructed with ``PHP_VAR_UNSERIALIZE_DESTROY``. It has pretty much 254 | the same function as its serialize equivalent: Storing variables for ``R``/``r``. 255 | 256 | In order to use the ``php_var_unserialize`` function we need two pointers to the serialized string: The first one is 257 | ``p``, which is the current position in the string. The second one is ``max`` and points to the end of the string. The 258 | ``p`` position is passed to ``php_var_unserialize`` by-reference and will be modified to point to the start of the next 259 | value that is to be unserialized. 260 | 261 | The first unserialization reads the buffer, the second the properties. The largest part of the code is various error 262 | handling. PHP has a long history of serialization related crashes (and security issues), so one should be careful to 263 | ensure all the data is valid. You should also not forget that methods like ``unserialize`` even though they have a 264 | special meaning can still called as normal methods. In order to prevent such calls the above call aborts if 265 | ``intern->buffer`` is already set. 266 | 267 | Now let's look at the second serialization mechanism, which will be used for the buffer views. In order to implement the 268 | ``O`` serialization we'll need a custom ``get_properties`` handler (which returns the "properties" to serialize) 269 | and a ``__wakeup`` method (which restores the state from the serialized properties). 270 | 271 | The ``get_properties`` handler allows you to fetch the properties of an object as a hashtable. The engine does this in 272 | various places, one of them being ``O`` serialization. Thus we can use this handler to return the view's buffer object, 273 | offset and length as properties, which will then be serialized just like any other property:: 274 | 275 | static HashTable *array_buffer_view_get_properties(zval *obj TSRMLS_DC) 276 | { 277 | buffer_view_object *intern = zend_object_store_get_object(obj TSRMLS_CC); 278 | HashTable *ht = zend_std_get_properties(obj TSRMLS_CC); 279 | zval *zv; 280 | 281 | if (!intern->buffer_zval) { 282 | return ht; 283 | } 284 | 285 | Z_ADDREF_P(intern->buffer_zval); 286 | zend_hash_update(ht, "buffer", sizeof("buffer"), &intern->buffer_zval, sizeof(zval *), NULL); 287 | 288 | MAKE_STD_ZVAL(zv); 289 | ZVAL_LONG(zv, intern->offset); 290 | zend_hash_update(ht, "offset", sizeof("offset"), &zv, sizeof(zval *), NULL); 291 | 292 | MAKE_STD_ZVAL(zv); 293 | ZVAL_LONG(zv, intern->length); 294 | zend_hash_update(ht, "length", sizeof("length"), &zv, sizeof(zval *), NULL); 295 | 296 | return ht; 297 | } 298 | 299 | Note that these magic properties will now also turn up in the debugging output, which in this case is probably a good 300 | idea. Also the properties will be accessible as "normal" properties, but only after this handler has been called. E.g. 301 | you would be able to access the ``$view->buffer`` property after serializing the object. We can't really do anything 302 | against this side-effect (other than using the other serialization method). 303 | 304 | In order to restore the state after unserialization we implement the ``__wakeup`` magic method. This method is called 305 | right after unserialization and allows you to read the object properties and reconstruct the internal state from them:: 306 | 307 | PHP_FUNCTION(array_buffer_view_wakeup) 308 | { 309 | buffer_view_object *intern; 310 | HashTable *props; 311 | zval **buffer_zv, **offset_zv, **length_zv; 312 | 313 | if (zend_parse_parameters_none() == FAILURE) { 314 | return; 315 | } 316 | 317 | intern = zend_object_store_get_object(getThis() TSRMLS_CC); 318 | 319 | if (intern->buffer_zval) { 320 | zend_throw_exception( 321 | NULL, "Cannot call __wakeup() on an already constructed object", 0 TSRMLS_CC 322 | ); 323 | return; 324 | } 325 | 326 | props = zend_std_get_properties(getThis() TSRMLS_CC); 327 | 328 | if (zend_hash_find(props, "buffer", sizeof("buffer"), (void **) &buffer_zv) == SUCCESS 329 | && zend_hash_find(props, "offset", sizeof("offset"), (void **) &offset_zv) == SUCCESS 330 | && zend_hash_find(props, "length", sizeof("length"), (void **) &length_zv) == SUCCESS 331 | && Z_TYPE_PP(buffer_zv) == IS_OBJECT 332 | && Z_TYPE_PP(offset_zv) == IS_LONG && Z_LVAL_PP(offset_zv) >= 0 333 | && Z_TYPE_PP(length_zv) == IS_LONG && Z_LVAL_PP(length_zv) > 0 334 | && instanceof_function(Z_OBJCE_PP(buffer_zv), array_buffer_ce TSRMLS_CC) 335 | ) { 336 | buffer_object *buffer_intern = zend_object_store_get_object(*buffer_zv TSRMLS_CC); 337 | size_t offset = Z_LVAL_PP(offset_zv), length = Z_LVAL_PP(length_zv); 338 | size_t bytes_per_element = buffer_view_get_bytes_per_element(intern); 339 | size_t max_length = (buffer_intern->length - offset) / bytes_per_element; 340 | 341 | if (offset < buffer_intern->length && length <= max_length) { 342 | Z_ADDREF_PP(buffer_zv); 343 | intern->buffer_zval = *buffer_zv; 344 | 345 | intern->offset = offset; 346 | intern->length = length; 347 | 348 | intern->buf.as_int8 = buffer_intern->buffer; 349 | intern->buf.as_int8 += offset; 350 | 351 | return; 352 | } 353 | } 354 | 355 | zend_throw_exception( 356 | NULL, "Invalid serialization data", 0 TSRMLS_CC 357 | ); 358 | } 359 | 360 | The method is more or less pure error-checking boilerplate (as is usual when dealing with serialization). The only 361 | thing it really does is to fetch the three magic properties using ``zend_hash_find``, check their validity and then 362 | initialize the internal object from them. 363 | 364 | Denying serialization 365 | --------------------- 366 | 367 | Sometimes objects can't be reasonably serialized. In this case you can deny serialization by assigning special 368 | serialization handlers:: 369 | 370 | ce->serialize = zend_class_serialize_deny; 371 | ce->unserialize = zend_class_unserialize_deny; 372 | 373 | The ``serialize`` and ``unserialize`` class handlers are used to implement the ``Serializable`` interface, i.e. the 374 | ``C`` serialization. As such assigning to them will deny serialization and ``C`` unserialization, but will still allow 375 | ``O`` unserialization. To disallow that case too, simply throw an error from ``__wakeup``:: 376 | 377 | PHP_METHOD(SomeClass, __wakeup) 378 | { 379 | if (zend_parse_parameters_none() == FAILURE) { 380 | return; 381 | } 382 | 383 | zend_throw_exception(NULL, "Unserialization of SomeClass is not allowed", 0 TSRMLS_CC); 384 | } 385 | 386 | And with this we leave the array buffers behind and turn towards magic interfaces as the next topic. -------------------------------------------------------------------------------- /Book/classes_objects/simple_classes.rst: -------------------------------------------------------------------------------- 1 | Basit sınıflar 2 | ============== 3 | 4 | Basic concepts 5 | -------------- 6 | 7 | Zvals store objects using the ``IS_OBJECT`` type tag and the ``zend_object_value`` structure in the union, which is 8 | defined as follows:: 9 | 10 | typedef struct _zend_object_value { 11 | zend_object_handle handle; 12 | const zend_object_handlers *handlers; 13 | } zend_object_value; 14 | 15 | The first part of the structure, the ``zend_object_handle``, is just a typedef for an unsigned integer. It is an ID 16 | uniquely identifying the object and is used to fetch the actual object data from the object store. 17 | 18 | The second part is a pointer to a structure of object handlers. These handlers define the actual behavior of the object. 19 | They cover everything from property fetches and method calls to custom comparison handling or even special garbage 20 | collection semantics. 21 | 22 | When called, the individual handlers get passed the object zval as the first argument, followed by various 23 | handler-specific information. They can then use the object handle to fetch the object data from the object store and do 24 | operations on it. 25 | 26 | The complementary structure to the object value is the class entry (``zend_class_entry``). Class entries contain a large 27 | amount of information, including the class methods and static properties as well as various handlers, in particular a 28 | handler for creating objects from the class. 29 | 30 | Class registration 31 | ------------------ 32 | 33 | Just like functions classes are registered in the extension's ``MINIT`` handler. Here is a snippet for declaring an 34 | empty ``Test`` class:: 35 | 36 | zend_class_entry *test_ce; 37 | 38 | const zend_function_entry test_functions[] = { 39 | PHP_FE_END 40 | }; 41 | 42 | PHP_MINIT_FUNCTION(test) 43 | { 44 | zend_class_entry tmp_ce; 45 | INIT_CLASS_ENTRY(tmp_ce, "Test", test_functions); 46 | 47 | test_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC); 48 | 49 | return SUCCESS; 50 | } 51 | 52 | The first line declares a global variable ``test_ce``, which will hold the class entry of the ``Test`` class. It is a 53 | "true" global variable (without thread safety protection) and should additionally be exported via the header file, so 54 | that other extensions can make use of the class. The following three lines declare an array for the class methods, just 55 | like you would do for normal functions. 56 | 57 | Then the main code follows: First a temporary class entry value ``tmp_ce`` is defined and then initialized using 58 | ``INIT_CLASS_ENTRY``. After that the class is registered in the Zend Engine using ``zend_register_internal_class``. This 59 | function also returns the final class entry, so it can be stored in the global variable declared above. 60 | 61 | To test that the class was registered properly you can run ``php --rc Test``, which should give an output along the 62 | following lines: 63 | 64 | .. code-block:: none 65 | 66 | Class [ class Test ] { 67 | - Constants [0] { 68 | } 69 | - Static properties [0] { 70 | } 71 | - Static methods [0] { 72 | } 73 | - Properties [0] { 74 | } 75 | - Methods [0] { 76 | } 77 | } 78 | 79 | As expected what you get is a totally empty class. 80 | 81 | Method definition and declaration 82 | --------------------------------- 83 | 84 | To bring it to life let's add a method:: 85 | 86 | PHP_METHOD(Test, helloWorld) /* {{{ */ 87 | { 88 | if (zend_parse_parameters_none() == FAILURE) { 89 | return; 90 | } 91 | 92 | RETURN_STRING("Hello World\n", 1); 93 | } 94 | /* }}} */ 95 | 96 | ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) 97 | ZEND_END_ARG_INFO() 98 | 99 | const zend_function_entry test_functions[] = { 100 | PHP_ME(Test, helloWorld, arginfo_void, ZEND_ACC_PUBLIC) 101 | PHP_FE_END 102 | }; 103 | 104 | As you can see a method declaration looks very similar to a function declaration. Instead of ``PHP_FUNCTION`` we use 105 | ``PHP_METHOD`` and pass it both the class and method name. In the ``zend_function_entry`` array ``PHP_ME`` is used 106 | instead of ``PHP_FE``. It again takes the class name, the method name, the arginfo struct and additionally a set of 107 | flags. 108 | 109 | The flags parameter allows you to specify the usual PHP method modifiers using a combination of ``ZEND_ACC_PUBLIC``, 110 | ``ZEND_ACC_PROTECTED``, ``ZEND_ACC_PRIVATE``, ``ZEND_ACC_STATIC``, ``ZEND_ACC_FINAL`` and ``ZEND_ACC_ABSTRACT``. For 111 | example a protected final static method would be declared as follows:: 112 | 113 | PHP_ME( 114 | Test, protectedFinalStaticMethod, arginfo_xyz, 115 | ZEND_ACC_PROTECTED | ZEND_ACC_FINAL | ZEND_ACC_STATIC 116 | ) 117 | 118 | As abstract methods do not have an associated implementation the ``ZEND_ACC_ABSTRACT`` flag is not used directly. 119 | Instead a special macro is provided:: 120 | 121 | PHP_ABSTRACT_ME(Test, abstractMethod, arginfo_abc) 122 | 123 | Analogous to what happens for ``PHP_FUNCTION`` the ``PHP_METHOD`` macro expands into a function declaration with a 124 | special name, which you may encounter when looking at backtraces within method calls:: 125 | 126 | PHP_METHOD(ClassName, methodName) { } 127 | /* expands to */ 128 | void zim_ClassName_methodName(INTERNAL_FUNCTION_PARAMETERS) { } 129 | 130 | But now, let's get back to writing methods. Here is another one:: 131 | 132 | PHP_METHOD(Test, getOwnObjectHandle) 133 | { 134 | zval *obj; 135 | 136 | if (zend_parse_parameters_none() == FAILURE) { 137 | return; 138 | } 139 | 140 | obj = getThis(); 141 | 142 | RETURN_LONG(Z_OBJ_HANDLE_P(obj)); 143 | } 144 | 145 | //... 146 | PHP_ME(Test, getOwnObjectHandle, arginfo_void, ZEND_ACC_PUBLIC) 147 | //... 148 | 149 | This method does nothing more than return the object's own object handle. To do this it first grabs the ``$this`` zval 150 | using the ``getThis()`` macro and then returns the object handle provided by ``Z_OBJ_HANDLE_P``. Try it out: 151 | 152 | .. code-block:: php 153 | 154 | $t1 = new Test; 155 | $other = new stdClass; 156 | $t2 = new Test; 157 | echo $t1, "\n", $t2, "\n"; 158 | 159 | This will (probably) output the numbers 1 and 3, so you can see that the object handle is basically just a number 160 | which is incremented with every new object. (This isn't exactly true because object handles can be reused again once the 161 | associated objects are destroyed.) 162 | 163 | Properties and constants 164 | ------------------------ 165 | 166 | To do something more useful, let's create two methods for reading from and writing to a property:: 167 | 168 | PHP_METHOD(Test, getFoo) 169 | { 170 | zval *obj, *foo_value; 171 | 172 | if (zend_parse_parameters_none() == FAILURE) { 173 | return; 174 | } 175 | 176 | obj = getThis(); 177 | 178 | foo_value = zend_read_property(test_ce, obj, "foo", sizeof("foo") - 1, 1 TSRMLS_CC); 179 | 180 | RETURN_ZVAL(foo_value, 1, 0); 181 | } 182 | 183 | PHP_METHOD(Test, setFoo) 184 | { 185 | zval *obj, *new_foo_value; 186 | 187 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &new_foo_value) == FAILURE) { 188 | return; 189 | } 190 | 191 | obj = getThis(); 192 | 193 | zend_update_property(test_ce, obj, "foo", sizeof("foo") - 1, new_foo_value TSRMLS_CC); 194 | } 195 | 196 | // ... 197 | 198 | ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) 199 | ZEND_END_ARG_INFO() 200 | 201 | ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 1) 202 | ZEND_ARG_INFO(0, value) 203 | ZEND_END_ARG_INFO() 204 | 205 | // ... 206 | PHP_ME(Test, getFoo, arginfo_void, ZEND_ACC_PUBLIC) 207 | PHP_ME(Test, setFoo, arginfo_set, ZEND_ACC_PUBLIC) 208 | // ... 209 | 210 | The two new functions in the above code are ``zend_read_property()`` and ``zend_update_property()``. Both functions take 211 | the scope as first parameter, the object as second and the property name and length after that. The "scope" here is 212 | a class entry and is necessary for visibility handling. If ``foo`` is a public property the used scope doesn't matter 213 | (it could just as well be ``NULL``), but if it were a private property we could only access it with the class entry of 214 | the class it belongs to. 215 | 216 | ``zend_update_property()`` additionally takes the new value for the property as last parameter. ``zend_read_property()`` 217 | on the other hand takes an additional boolean ``silent`` parameter. It specifies whether PHP should suppress the 218 | "Undefined property xyz" notice. In our case we don't know whether the property exists beforehand, so we pass ``1`` 219 | (meaning: no notice). 220 | 221 | We can try the new functionality out: 222 | 223 | .. code-block:: php 224 | 225 | $t = new Test; 226 | var_dump($t->getFoo()); // NULL (and no notice, because we passed silent=1) 227 | 228 | $t->setFoo("abc"); 229 | var_dump($t->foo); // "abc" 230 | var_dump($t->getFoo()); // "abc" 231 | 232 | $t->foo = "def"; 233 | var_dump($t->foo); // "def" 234 | var_dump($t->getFoo()); // "def" 235 | 236 | ``zend_update_property()`` also comes in several variants that allow setting specific values more easily (i.e. without 237 | manually creating a zval): 238 | 239 | * ``zend_update_property_null(... TSRMLS_DC)`` 240 | * ``zend_update_property_bool(..., long value TSRMLS_DC)`` 241 | * ``zend_update_property_long(..., long value TSRMLS_DC)`` 242 | * ``zend_update_property_double(..., double value TSRMLS_DC)`` 243 | * ``zend_update_property_string(..., const char *value TSRMLS_DC)`` 244 | * ``zend_update_property_stringl(..., const char *value, int value_len TSRMLS_DC)`` 245 | 246 | In the above example we had to use the ``silent=1`` parameter, because we didn't have the guarantee that the ``foo`` 247 | property would exist when we read it. A better way to solve this is to properly declare the property when the class is 248 | registered, just like you would write ``public $foo = DEFAULT_VALUE;`` in PHP. 249 | 250 | This is done using the ``zend_declare_property()`` function family, which features the same variants as 251 | ``zend_update_property()``. For example to declare a public ``foo`` property with a ``null`` default value we have to add 252 | the following line after the class registration in ``MINIT``:: 253 | 254 | zend_declare_property_null(test_ce, "foo", sizeof("foo") - 1, ZEND_ACC_PUBLIC TSRMLS_CC); 255 | 256 | To create a protected property defaulting to the string ``"bar"`` you instead write:: 257 | 258 | zend_declare_property_string( 259 | test_ce, "foo", sizeof("foo") - 1, "bar", ZEND_ACC_PROTECTED TSRMLS_CC 260 | ); 261 | 262 | If you want to use properties (and you will soon find that this is only rarely necessary for internal classes) it is 263 | always good practice to properly declare properties. This way you have an explicit visibility level, a default value 264 | and you also benefit from memory optimizations for declared properties. 265 | 266 | Static properties are also declared using the same family of functions by additionally specifying the 267 | ``ZEND_ACC_STATIC`` flag. A public static ``$pi`` property:: 268 | 269 | zend_declare_property_double( 270 | test_ce, "pi", sizeof("pi") - 1, 3.141, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC TSRMLS_CC 271 | ); 272 | /* All digits of pi I remember :( */ 273 | 274 | To read and update static properties there are the ``zend_read_static_property()`` function and the 275 | ``zend_update_static_property()`` function family. They have the same interface as the functions for normal properties, 276 | only difference being that no object is passed (only the scope). 277 | 278 | To declare constants the ``zend_declare_class_constant_*()`` family of functions is used. They have the same variations and 279 | signatures as ``zend_declare_property_*()``, only without the flags argument. To declare a constant ''Test::PI'':: 280 | 281 | zend_declare_class_constant_double(test_ce, "PI", sizeof("PI") - 1, 3.141 TSRMLS_CC); 282 | 283 | Inheritance and interfaces 284 | -------------------------- 285 | 286 | Just like their userland equivalents internal classes can also inherit from other classes and/or implement interfaces. 287 | 288 | A very simple (and quite common) example of inheritance in the PHP tree is creating some custom subtype of 289 | ``Exception``:: 290 | 291 | zend_class_entry *custom_exception_ce; 292 | 293 | PHP_MINIT_FUNCTION(Test) 294 | { 295 | zend_class_entry tmp_ce; 296 | INIT_CLASS_ENTRY(tmp_ce, "CustomException", NULL); 297 | custom_exception_ce = zend_register_internal_class_ex( 298 | &tmp_ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC 299 | ); 300 | 301 | return SUCCESS; 302 | } 303 | 304 | The new thing here is the use of ``zend_register_internal_class_ex()`` (with the ``_ex``), which does the same thing as 305 | ``zend_register_internal_class()``, but additionally allows you to specify the parent class entry. Here the parent CE is 306 | fetched using ``zend_exception_get_default(TSRMLS_C)``. Another detail worth pointing out is that we did not define a 307 | function structure and instead just passed ``NULL`` as the last argument to ``INIT_CLASS_ENTRY``. This means that we 308 | don't want any additional methods, apart from those that are inherited from ``Exception``. 309 | 310 | If you want to extend a more specific SPL extension class like ``RuntimeException`` you can also do so:: 311 | 312 | #ifdef HAVE_SPL 313 | #include "ext/spl/spl_exceptions.h" 314 | #endif 315 | 316 | zend_class_entry *custom_exception_ce; 317 | 318 | PHP_MINIT_FUNCTION(Test) 319 | { 320 | zend_class_entry tmp_ce; 321 | INIT_CLASS_ENTRY(tmp_ce, "CustomException", NULL); 322 | 323 | #ifdef HAVE_SPL 324 | custom_exception_ce = zend_register_internal_class_ex( 325 | &tmp_ce, spl_ce_RuntimeException, NULL TSRMLS_CC 326 | ); 327 | #else 328 | custom_exception_ce = zend_register_internal_class_ex( 329 | &tmp_ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC 330 | ); 331 | #endif 332 | 333 | return SUCCESS; 334 | } 335 | 336 | The above code conditionally either inherits from ``RuntimeException`` or - if SPL is not compiled in - from just 337 | ``Exception``. The class entry for ``RuntimeException`` is externed in the header ``ext/spl/spl_exceptions.h``, so it 338 | has to be included as well. 339 | 340 | The last parameter of ``zend_register_internal_class_ex()`` which was set to ``NULL`` in the above cases, is an 341 | alternative way to specify the parent class: If you don't have the class entry available you can specify the class 342 | name:: 343 | 344 | custom_exception_ce = zend_register_internal_class_ex( 345 | &tmp_ce, spl_ce_RuntimeException, NULL TSRMLS_CC 346 | ); 347 | /* can also be written as */ 348 | custom_exception_ce = zend_register_internal_class_ex( 349 | &tmp_ce, NULL, "RuntimeException" TSRMLS_CC 350 | ); 351 | 352 | In practice you should prefer the first variant though. The second form is only useful if you have some misbehaved 353 | extension that forgot to export the class entry. 354 | 355 | Just like you can inherit from other classes you can also implement interfaces. For this the variadic 356 | ``zend_class_implements()`` functions is used:: 357 | 358 | #include "ext/spl/spl_iterators.h" 359 | #include "zend_interfaces.h" 360 | 361 | zend_class_entry *data_class_ce; 362 | 363 | PHP_METHOD(DataClass, count) { /* ... */ } 364 | 365 | const zend_function_entry data_class_functions[] = { 366 | PHP_ME(DataClass, count, arginfo_void, ZEND_ACC_PUBLIC) 367 | /* ... */ 368 | PHP_FE_END 369 | }; 370 | 371 | PHP_MINIT_FUNCTION(test) 372 | { 373 | zend_class_entry tmp_ce; 374 | INIT_CLASS_ENTRY(tmp_ce, "DataClass", data_class_functions); 375 | data_class_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC); 376 | 377 | /* DataClass implements Countable, ArrayAccess, IteratorAggregate */ 378 | zend_class_implements( 379 | data_class_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_arrayaccess, zend_ce_aggregate 380 | ); 381 | 382 | return SUCCESS; 383 | } 384 | 385 | As you can see ``zend_class_implements()`` takes the class entry, TSRMLS_CC, the number of interfaces to implement and 386 | then the class entries of the interfaces. E.g. if you wanted to additionally implement ``Serializable``:: 387 | 388 | zend_class_implements( 389 | data_class_ce TSRMLS_CC, 4, 390 | spl_ce_Countable, zend_ce_arrayaccess, zend_ce_aggregate, zend_ce_serializable 391 | ); 392 | 393 | You can obviously also create your own interfaces. Interfaces are registered in the same way as classes are, but using 394 | the ``zend_register_internal_interface()`` function and declaring all methods as abstract. E.g. if you wanted to create a 395 | new ``ReversibleIterator`` interface that extends ``Iterator`` and additionally specifies a ``prev`` method, this is how 396 | you would do it:: 397 | 398 | #include "zend_interfaces.h" 399 | 400 | zend_class_entry *reversible_iterator_ce; 401 | 402 | const zend_function_entry reversible_iterator_functions[] = { 403 | PHP_ABSTRACT_ME(ReversibleIterator, prev, arginfo_void) 404 | PHP_FE_END 405 | }; 406 | 407 | PHP_MINIT_FUNCTION(test) 408 | { 409 | zend_class_entry tmp_ce; 410 | INIT_CLASS_ENTRY(tmp_ce, "ReversibleIterator", reversible_iterator_functions); 411 | reversible_iterator_ce = zend_register_internal_interface(&tmp_ce TSRMLS_CC); 412 | 413 | /* ReversibleIterator extends Iterator. For interface inheritance the zend_class_implements() 414 | * function is used. */ 415 | zend_class_implements(reversible_iterator_ce TSRMLS_CC, 1, zend_ce_iterator); 416 | 417 | return SUCCESS; 418 | } 419 | 420 | Internal interfaces have a bit of additional power that userland interfaces don't have - but I'll leave that for a bit 421 | later. 422 | -------------------------------------------------------------------------------- /Book/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # PHPInternalsBook documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Oct 26 14:36:43 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | from sphinx.highlighting import lexers 16 | from pygments.lexers.web import PhpLexer 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- PHP code highlights configuration ----------------------------------------- 24 | 25 | lexers['php'] = PhpLexer(startinline=True) 26 | lexers['php-annotations'] = PhpLexer(startinline=True) 27 | 28 | # -- General configuration ----------------------------------------------------- 29 | 30 | # If your documentation needs a minimal Sphinx version, state it here. 31 | #needs_sphinx = '1.0' 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be extensions 34 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 35 | extensions = ['sphinx.ext.todo', 'sphinx.ext.ifconfig'] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix of source filenames. 41 | source_suffix = '.rst' 42 | 43 | # The encoding of source files. 44 | #source_encoding = 'utf-8-sig' 45 | 46 | # The master toctree document. 47 | master_doc = 'index' 48 | 49 | # General information about the project. 50 | project = u'PHPInternalsBook' 51 | copyright = u'2013, Julien Pauli - Anthony Ferrara - Nikita Popov' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '1.0' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '1.0' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | language = 'tr' 65 | 66 | # There are two options for replacing |today|: either, you set today to some 67 | # non-false value, then it is used: 68 | #today = '' 69 | # Else, today_fmt is used as the format for a strftime call. 70 | #today_fmt = '%B %d, %Y' 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | exclude_patterns = ['TODO'] 75 | 76 | # The reST default role (used for this markup: `text`) to use for all documents. 77 | #default_role = None 78 | 79 | # If true, '()' will be appended to :func: etc. cross-reference text. 80 | #add_function_parentheses = True 81 | 82 | # If true, the current module name will be prepended to all description 83 | # unit titles (such as .. function::). 84 | #add_module_names = True 85 | 86 | # If true, sectionauthor and moduleauthor directives will be shown in the 87 | # output. They are ignored by default. 88 | #show_authors = False 89 | 90 | # Code included with :: will be C (and not Python) 91 | highlight_language = 'c' 92 | 93 | # The name of the Pygments (syntax highlighting) style to use. 94 | pygments_style = 'sphinx' 95 | 96 | # A list of ignored prefixes for module index sorting. 97 | #modindex_common_prefix = [] 98 | 99 | 100 | # -- Options for HTML output --------------------------------------------------- 101 | 102 | # The theme to use for HTML and HTML Help pages. See the documentation for 103 | # a list of builtin themes. 104 | html_theme = 'haiku' 105 | 106 | # Theme options are theme-specific and customize the look and feel of a theme 107 | # further. For a list of options available for each theme, see the 108 | # documentation. 109 | #html_theme_options = {} 110 | 111 | # Add any paths that contain custom themes here, relative to this directory. 112 | #html_theme_path = [] 113 | 114 | # The name for this set of Sphinx documents. If None, it defaults to 115 | # " v documentation". 116 | html_title = 'PHP Internals Book' 117 | 118 | # A shorter title for the navigation bar. Default is the same as html_title. 119 | #html_short_title = None 120 | 121 | # The name of an image file (relative to this directory) to place at the top 122 | # of the sidebar. 123 | #html_logo = None 124 | 125 | # The name of an image file (within the static path) to use as favicon of the 126 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 127 | # pixels large. 128 | #html_favicon = None 129 | 130 | # Add any paths that contain custom static files (such as style sheets) here, 131 | # relative to this directory. They are copied after the builtin static files, 132 | # so a file named "default.css" will overwrite the builtin "default.css". 133 | html_static_path = ['_static'] 134 | 135 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 136 | # using the given strftime format. 137 | #html_last_updated_fmt = '%b %d, %Y' 138 | 139 | # If true, SmartyPants will be used to convert quotes and dashes to 140 | # typographically correct entities. 141 | #html_use_smartypants = True 142 | 143 | # Custom sidebar templates, maps document names to template names. 144 | #html_sidebars = {} 145 | 146 | # Additional templates that should be rendered to pages, maps page names to 147 | # template names. 148 | #html_additional_pages = {} 149 | 150 | # If false, no module index is generated. 151 | #html_domain_indices = True 152 | 153 | # If false, no index is generated. 154 | #html_use_index = True 155 | 156 | # If true, the index is split into individual pages for each letter. 157 | #html_split_index = False 158 | 159 | # If true, links to the reST sources are added to the pages. 160 | #html_show_sourcelink = True 161 | 162 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 163 | html_show_sphinx = False 164 | 165 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 166 | #html_show_copyright = True 167 | 168 | # If true, an OpenSearch description file will be output, and all pages will 169 | # contain a tag referring to it. The value of this option must be the 170 | # base URL from which the finished HTML is served. 171 | #html_use_opensearch = '' 172 | 173 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 174 | #html_file_suffix = None 175 | 176 | # Output file base name for HTML help builder. 177 | htmlhelp_basename = 'PHPInternalsBookdoc' 178 | 179 | 180 | # -- Options for LaTeX output -------------------------------------------------- 181 | 182 | # Use smaller font for code listings in PDF, so that there is about 100 chars per line 183 | from sphinx.highlighting import PygmentsBridge 184 | from pygments.formatters.latex import LatexFormatter 185 | 186 | class CustomLatexFormatter(LatexFormatter): 187 | def __init__(self, **options): 188 | super(CustomLatexFormatter, self).__init__(**options) 189 | self.verboptions = r"formatcom=\footnotesize" 190 | 191 | PygmentsBridge.latex_formatter = CustomLatexFormatter 192 | 193 | latex_elements = { 194 | # The paper size ('letterpaper' or 'a4paper'). 195 | #'papersize': 'letterpaper', 196 | 197 | # The font size ('10pt', '11pt' or '12pt'). 198 | #'pointsize': '10pt', 199 | 200 | # Additional stuff for the LaTeX preamble. 201 | #'preamble': '', 202 | } 203 | 204 | # Grouping the document tree into LaTeX files. List of tuples 205 | # (source start file, target name, title, author, documentclass [howto/manual]). 206 | latex_documents = [ 207 | ('index', 'PHPInternalsBook.tex', u'PHPInternalsBook Documentation', 208 | u'Julien Pauli - Anthony Ferrara - Nikita Popov', 'manual'), 209 | ] 210 | 211 | # The name of an image file (relative to this directory) to place at the top of 212 | # the title page. 213 | #latex_logo = None 214 | 215 | # For "manual" documents, if this is true, then toplevel headings are parts, 216 | # not chapters. 217 | #latex_use_parts = False 218 | 219 | # If true, show page references after internal links. 220 | #latex_show_pagerefs = False 221 | 222 | # If true, show URL addresses after external links. 223 | #latex_show_urls = False 224 | 225 | # Documents to append as an appendix to all manuals. 226 | #latex_appendices = [] 227 | 228 | # If false, no module index is generated. 229 | #latex_domain_indices = True 230 | 231 | 232 | # -- Options for manual page output -------------------------------------------- 233 | 234 | # One entry per manual page. List of tuples 235 | # (source start file, name, description, authors, manual section). 236 | man_pages = [ 237 | ('index', 'phpinternalsbook', u'PHPInternalsBook Documentation', 238 | [u'Julien Pauli - Anthony Ferrara - Nikita Popov'], 1) 239 | ] 240 | 241 | # If true, show URL addresses after external links. 242 | #man_show_urls = False 243 | 244 | 245 | # -- Options for Texinfo output ------------------------------------------------ 246 | 247 | # Grouping the document tree into Texinfo files. List of tuples 248 | # (source start file, target name, title, author, 249 | # dir menu entry, description, category) 250 | texinfo_documents = [ 251 | ('index', 'PHPInternalsBook', u'PHPInternalsBook Documentation', 252 | u'Julien Pauli - Anthony Ferrara - Nikita Popov', 'PHPInternalsBook', 'One line description of project.', 253 | 'Miscellaneous'), 254 | ] 255 | 256 | # Documents to append as an appendix to all manuals. 257 | #texinfo_appendices = [] 258 | 259 | # If false, no module index is generated. 260 | #texinfo_domain_indices = True 261 | 262 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 263 | #texinfo_show_urls = 'footnote' 264 | 265 | 266 | # -- Options for Epub output --------------------------------------------------- 267 | 268 | # Bibliographic Dublin Core info. 269 | epub_title = u'PHPInternalsBook' 270 | epub_author = u'Julien Pauli - Anthony Ferrara - Nikita Popov' 271 | epub_publisher = u'Julien Pauli - Anthony Ferrara - Nikita Popov' 272 | epub_copyright = u'2012, Julien Pauli - Anthony Ferrara - Nikita Popov' 273 | 274 | # The language of the text. It defaults to the language option 275 | # or en if the language is not set. 276 | #epub_language = '' 277 | 278 | # The scheme of the identifier. Typical schemes are ISBN or URL. 279 | #epub_scheme = '' 280 | 281 | # The unique identifier of the text. This can be a ISBN number 282 | # or the project homepage. 283 | #epub_identifier = '' 284 | 285 | # A unique identification for the text. 286 | #epub_uid = '' 287 | 288 | # A tuple containing the cover image and cover page html template filenames. 289 | #epub_cover = () 290 | 291 | # HTML files that should be inserted before the pages created by sphinx. 292 | # The format is a list of tuples containing the path and title. 293 | #epub_pre_files = [] 294 | 295 | # HTML files shat should be inserted after the pages created by sphinx. 296 | # The format is a list of tuples containing the path and title. 297 | #epub_post_files = [] 298 | 299 | # A list of files that should not be packed into the epub file. 300 | #epub_exclude_files = [] 301 | 302 | # The depth of the table of contents in toc.ncx. 303 | #epub_tocdepth = 3 304 | 305 | # Allow duplicate toc entries. 306 | #epub_tocdup = True 307 | -------------------------------------------------------------------------------- /Book/hashtables.rst: -------------------------------------------------------------------------------- 1 | Hash tabloları 2 | ============== 3 | 4 | Hashtables are one of the most important structures used by PHP. They form the basis for arrays, object properties, 5 | symbol tables and have countless other applications throughout the engine. This chapter will introduce how hashtables 6 | work and cover the related APIs in detail. 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | hashtables/basic_structure.rst 14 | hashtables/hashtable_api.rst 15 | hashtables/array_api.rst 16 | hashtables/hash_algorithm.rst -------------------------------------------------------------------------------- /Book/hashtables/array_api.rst: -------------------------------------------------------------------------------- 1 | Symtable ve array API 2 | ====================== 3 | 4 | The hashtable API allows you to work with values of any type, but in the vast majority of cases the values will be 5 | zvals. Using the ``zend_hash`` API with zvals can often be somewhat cumbersome, as you need to handle zval allocation 6 | and initialization yourself. This is why PHP provides a second set of APIs specifically aimed at this use case. Before 7 | introducing these simplified APIs we will have a look at a special kind of hashtable that PHP arrays make use of. 8 | 9 | Symtables 10 | --------- 11 | 12 | One of the core concepts behind the design of PHP is that integers and strings containing integers should be 13 | interchangeable. This also applies to arrays where the keys ``42`` and ``"42"`` should be considered the same. This is 14 | not the case though with ordinary hashtables: They strictly distinguish the key types and it's okay to have both the 15 | key ``42`` and ``"42"`` in the same table (with different values). 16 | 17 | This is why there is an additional *symtable* (symbol table) API, which is a thin wrapper around some hashtable 18 | functions which converts integral string keys to actual integer keys. For example, this is how the 19 | ``zend_symtable_find()`` function is defined:: 20 | 21 | static inline int zend_symtable_find( 22 | HashTable *ht, const char *arKey, uint nKeyLength, void **pData 23 | ) { 24 | ZEND_HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_find(ht, idx, pData)); 25 | return zend_hash_find(ht, arKey, nKeyLength, pData); 26 | } 27 | 28 | The implementation of the ``ZEND_HANDLE_NUMERIC()`` macro will not be considered in detail here, only the functionality 29 | behind it is important: If ``arKey`` contains a decimal integer between ``LONG_MIN`` and ``LONG_MAX``, then that 30 | integer is written into ``idx`` and ``zend_hash_index_find()`` is called with it. In all other cases the code will 31 | continue to the next line, where ``zend_hash_find()`` will be invoked. 32 | 33 | Apart from ``zend_symtable_find()`` the following functions are part of the symtable API, again with the same behavior 34 | as their hashtable counterparts, but including string to integer normalization:: 35 | 36 | static inline int zend_symtable_exists(HashTable *ht, const char *arKey, uint nKeyLength); 37 | static inline int zend_symtable_del(HashTable *ht, const char *arKey, uint nKeyLength); 38 | static inline int zend_symtable_update( 39 | HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest 40 | ); 41 | static inline int zend_symtable_update_current_key_ex( 42 | HashTable *ht, const char *arKey, uint nKeyLength, int mode, HashPosition *pos 43 | ); 44 | 45 | Additionally there are two macros for creating symtables:: 46 | 47 | #define ZEND_INIT_SYMTABLE_EX(ht, n, persistent) \ 48 | zend_hash_init(ht, n, NULL, ZVAL_PTR_DTOR, persistent) 49 | 50 | #define ZEND_INIT_SYMTABLE(ht) \ 51 | ZEND_INIT_SYMTABLE_EX(ht, 2, 0) 52 | 53 | As you can see these macros are just ``zend_hash_init()`` calls using ``ZVAL_PTR_DTOR`` as the destructor. As such 54 | these macros are not directly related to the string to integer casting behavior described above. 55 | 56 | Let's give this new set of functions a try:: 57 | 58 | HashTable *myht; 59 | zval *zv1, *zv2; 60 | zval **zv_dest; 61 | 62 | ALLOC_HASHTABLE(myht); 63 | ZEND_INIT_SYMTABLE(myht); 64 | 65 | MAKE_STD_ZVAL(zv1); 66 | ZVAL_STRING(zv1, "zv1", 1); 67 | 68 | MAKE_STD_ZVAL(zv2); 69 | ZVAL_STRING(zv2, "zv2", 1); 70 | 71 | zend_hash_index_update(myht, 42, &zv1, sizeof(zval *), NULL); 72 | zend_symtable_update(myht, "42", sizeof("42"), &zv2, sizeof(zval *), NULL); 73 | 74 | if (zend_hash_index_find(myht, 42, (void **) &zv_dest) == SUCCESS) { 75 | php_printf("Value at key 42 is %Z\n", *zv_dest); 76 | } 77 | 78 | if (zend_symtable_find(myht, "42", sizeof("42"), (void **) &zv_dest) == SUCCESS) { 79 | php_printf("Value at key \"42\" is %Z\n", *zv_dest); 80 | } 81 | 82 | zend_hash_destroy(myht); 83 | FREE_HASHTABLE(myht); 84 | 85 | This code will print: 86 | 87 | .. code-block:: none 88 | 89 | Value at key 42 is zv2 90 | Value at key "42" is zv2 91 | 92 | Thus both ``update`` calls wrote to the same element (the second one overwriting the first one) and both ``find`` calls 93 | also found the same element. 94 | 95 | Array API 96 | --------- 97 | 98 | Now we have all the prerequisites to look at the array API. This API no longer works directly on hashtables, but rather 99 | accepts zvals from which the hashtable is extracted using ``Z_ARRVAL_P()``. 100 | 101 | The first two functions from this API are ``array_init()`` and ``array_init_size()``, which initialize a hashtable 102 | into a zval. The former function takes only the target zval, whereas the latter takes an additional size hint:: 103 | 104 | /* Create empty array into return_value */ 105 | array_init(return_value); 106 | 107 | /* Create empty array with expected size 1000000 into return_value */ 108 | array_init_size(return_value, 1000000); 109 | 110 | The remaining functions of this API all deal with inserting values into an array. There are four families of functions 111 | which look as follows:: 112 | 113 | /* Insert at next index */ 114 | int add_next_index_*(zval *arg, ...); 115 | /* Insert at specific index */ 116 | int add_index_*(zval *arg, ulong idx, ...); 117 | /* Insert at specific key */ 118 | int add_assoc_*(zval *arg, const char *key, ...); 119 | /* Insert at specific key of length key_len (for binary safety) */ 120 | int add_assoc_*_ex(zval *arg, const char *key, uint key_len, ...); 121 | 122 | Here ``*`` is a placeholder for a type and ``...`` a placeholder for the type-specific arguments. The valid values for 123 | them are listed in the following table: 124 | 125 | .. list-table:: 126 | :header-rows: 1 127 | :widths: 8 20 128 | 129 | * - Type 130 | - Additional arguments 131 | * - ``null`` 132 | - none 133 | * - ``bool`` 134 | - ``int b`` 135 | * - ``long`` 136 | - ``long n`` 137 | * - ``double`` 138 | - ``double d`` 139 | * - ``string`` 140 | - ``const char *str, int duplicate`` 141 | * - ``stringl`` 142 | - ``const char *str, uint length, int duplicate`` 143 | * - ``resource`` 144 | - ``int r`` 145 | * - ``zval`` 146 | - ``zval *value`` 147 | 148 | As an example for the usage of these functions, let's just create a dummy array with elements of various types:: 149 | 150 | PHP_FUNCTION(make_array) { 151 | zval *zv; 152 | 153 | array_init(return_value); 154 | 155 | add_index_long(return_value, 10, 100); 156 | add_index_double(return_value, 20, 3.141); 157 | add_index_string(return_value, 30, "foo", 1); 158 | 159 | add_next_index_bool(return_value, 1); 160 | add_next_index_stringl(return_value, "\0bar", sizeof("\0bar")-1, 1); 161 | 162 | add_assoc_null(return_value, "foo"); 163 | add_assoc_long(return_value, "bar", 42); 164 | 165 | add_assoc_double_ex(return_value, "\0bar", sizeof("\0bar"), 1.61); 166 | 167 | /* For some things you still have to manually create a zval... */ 168 | MAKE_STD_ZVAL(zv); 169 | object_init(zv); 170 | add_next_index_zval(return_value, zv); 171 | } 172 | 173 | The ``var_dump()`` output of this array looks as follows (with NUL-bytes replaced by ``\0``): 174 | 175 | .. code-block:: none 176 | 177 | array(9) { 178 | [10]=> 179 | int(100) 180 | [20]=> 181 | float(3.141) 182 | [30]=> 183 | string(3) "foo" 184 | [31]=> 185 | bool(true) 186 | [32]=> 187 | string(4) "\0bar" 188 | ["foo"]=> 189 | NULL 190 | ["bar"]=> 191 | int(42) 192 | ["\0bar"]=> 193 | float(1.61) 194 | [33]=> 195 | object(stdClass)#1 (0) { 196 | } 197 | } 198 | 199 | Looking at the above code you may notice that the array API is even more inconsistent in regard to string lengths: The 200 | key length passed to the ``_ex`` functions *includes* the terminating NUL-byte, whereas the string length passed to the 201 | ``stringl`` functions *excludes* the NUL-byte. 202 | 203 | Furthermore it should be noted that while these functions start with ``add`` they behave like ``update`` functions in 204 | that they overwrite previously existing keys. 205 | 206 | There are several additional ``add_get`` functions which both insert a value and fetch it again (analogous to the last 207 | parameter of the ``zend_hash_update`` functions). As they are virtually never used they will not be discussed here and 208 | are mentioned only for the sake of completeness. 209 | 210 | This concludes our walk through the hashtable, symtable and array APIs. -------------------------------------------------------------------------------- /Book/hashtables/basic_structure.rst: -------------------------------------------------------------------------------- 1 | Temel yapı 2 | =============== 3 | 4 | Basic concepts 5 | -------------- 6 | 7 | Arrays in C are just regions of memory that can be accessed by offset. This implies that keys have to be integers and 8 | need to be continuous. For example, if you have the keys 0, 1 and 2, then the next key has to be 3 and can't be 9 | 214678462. PHP arrays are very different: They support both string keys and non-continuous integer keys and even allow 10 | mixing both. 11 | 12 | To implement such a structure in C there are two approaches: The first is using a binary search tree, where both lookup 13 | and insert have complexity ``O(log n)`` (where ``n`` is the number of elements in the table). The second is a hashtable, 14 | which has an average lookup/insert complexity of ``O(1)``, i.e. elements can be inserted and retrieved in constant time. 15 | As such hashtables are preferable in most cases and are also the technique that PHP uses. 16 | 17 | The idea behind a hashtable is very simple: A complex key value (like a string) is converted into an integer using a 18 | hash function. This integer can then be used as an offset into a normal C array. The issue is that the number of 19 | integers (``2^32`` or ``2^64``) is much smaller than the number of strings (of which there are infinitely many). As such 20 | the hash function will have collisions, i.e. cases where two strings have the same hash value. 21 | 22 | As such some kind of collision resolution has to take place. There are basically two solutions to this problem, the 23 | first being *open addressing* (which is not covered here). The second one is *chaining* and is employed by PHP. This 24 | method simply stores all elements having the same hash in a linked list. When a key is looked up PHP will calculate the 25 | hash and then go through the linked list of "possible" values until it finds the matching entry. Here is an 26 | illustration of chaining collision resolution: 27 | 28 | .. image:: ./images/basic_hashtable.* 29 | :align: center 30 | :height: 265px 31 | :scale: 200% 32 | 33 | The elements of the linked list are called ``Bucket``\s and the C array containing the heads of the linked lists is 34 | called ``arBuckets``. 35 | 36 | Consider how you would delete an element from such a structure: Say you have a pointer to the bucket of ``"c"`` and want 37 | to remove it. To do this you'd have to set the pointer coming from ``"a"`` to ``NULL``. Thus you need to retrieve the 38 | bucket of ``"a"`` which you can do either by traversing the linked list for the hash value or by additionally storing 39 | pointers in the reverse direction. The latter is what PHP does: Every bucket contains both a pointer to the next bucket 40 | (``pNext``) and the previous bucket (``pLast``). This is illustrated in the following graphic: 41 | 42 | .. image:: ./images/doubly_linked_hashtable.* 43 | :align: center 44 | :height: 250px 45 | :scale: 200% 46 | 47 | Furthermore PHP hashtables are *ordered*: If you traverse an array you'll get the elements in same order in which you 48 | inserted them. To support this the buckets have to be part of another linked list which specifies the order. This is 49 | once again a doubly linked list, for the same reasons as outlined above (and to support traversation in reverse order). 50 | The forward pointers are stored in ``pListNext``, the backward pointers in ``pListLast``. Additionally the hashtable 51 | structure has a pointer to the start of the list (``pListHead``) and the end of the list (``pListLast``). Here is an 52 | example of how this linked list could look like for the elements ``"a"``, ``"b"``, ``"c"`` (in that order): 53 | 54 | .. image:: ./images/ordered_hashtable.* 55 | :align: center 56 | :height: 250px 57 | :scale: 200% 58 | 59 | The HashTable and Bucket structures 60 | ----------------------------------- 61 | 62 | To implement hashtables PHP uses two structures, which can be found in the ``zend_hash.h`` file. We'll first have a look 63 | at the ``Bucket`` struct:: 64 | 65 | typedef struct bucket { 66 | ulong h; 67 | uint nKeyLength; 68 | void *pData; 69 | void *pDataPtr; 70 | struct bucket *pListNext; 71 | struct bucket *pListLast; 72 | struct bucket *pNext; 73 | struct bucket *pLast; 74 | char *arKey; 75 | } Bucket; 76 | 77 | You already know what the ``pNext``, ``pLast``, ``pListNext`` and ``pListLast`` pointers are for. Let's quickly go 78 | through the remaining members: 79 | 80 | ``h`` is the hash of the key. If the key is an integer, then ``h`` will be that integer (for integers the hash function 81 | doesn't do anything) and ``nKeyLength`` will be 0. For string keys ``h`` will be the result of ``zend_hash_func()``, 82 | ``arKey`` will hold the string and ``nKeyLength`` its length. 83 | 84 | ``pData`` is a pointer to the stored value. The stored value will not be the same as the one passed to the insertion 85 | function, rather it will be a copy of it (which is allocated separately from the bucket). As this would be very 86 | inefficient when the stored value is a pointer PHP employs a small trick: Instead of storing the pointer in a separate 87 | allocation it is put into the ``pDataPtr`` member. ``pData`` then points to that member (``pData = &pDataPtr``). 88 | 89 | Let's have a look at the main ``HashTable`` struct now:: 90 | 91 | typedef struct _hashtable { 92 | uint nTableSize; 93 | uint nTableMask; 94 | uint nNumOfElements; 95 | ulong nNextFreeElement; 96 | Bucket *pInternalPointer; 97 | Bucket *pListHead; 98 | Bucket *pListTail; 99 | Bucket **arBuckets; 100 | dtor_func_t pDestructor; 101 | zend_bool persistent; 102 | unsigned char nApplyCount; 103 | zend_bool bApplyProtection; 104 | #if ZEND_DEBUG 105 | int inconsistent; 106 | #endif 107 | } HashTable; 108 | 109 | You already know that ``arBuckets`` is the C array that contains the linked bucket lists and is indexed by the hash of 110 | the key. As PHP arrays don't have a fixed size ``arBuckets`` has to be dynamically resized when the number of elements 111 | in the table (``nNumOfElements``) surpasses the current size of the ``arBuckets`` allocation (``nTableSize``). We could 112 | of course store more than ``nTableSize`` elements in the hashtable, but this would increase the number of collisions 113 | and thus degrade performance. 114 | 115 | ``nTableSize`` is always a power of two, so if you have 12 elements in a hashtable the actual table size will be 16. 116 | Note though that while the ``arBuckets`` array automatically grows, it will *not* shrink when you remove elements. If 117 | you first insert 1000000 elements into the hashtable and then remove all of them again the ``nTableSize`` will still 118 | be 1048576. 119 | 120 | The result of the hash function is a ``ulong``, but the ``nTableSize`` will usually be a lot smaller than that. Thus 121 | the hash can not be directly used to index into the ``arBuckets`` array. Instead ``nIndex = h % nTableSize`` is used. 122 | As the table size is always a power of two this expression is equivalent to ``nIndex = h & (nTableSize - 1)``. To see 123 | why let's see how ``nTableSize - 1`` changes the value: 124 | 125 | .. code-block:: none 126 | 127 | nTableSize = 128 = 0b00000000.00000000.00000000.10000000 128 | nTableSize - 1 = 127 = 0b00000000.00000000.00000000.01111111 129 | 130 | ``nTableSize - 1`` has all bits below the table size set. Thus doing ``h & (nTableSize - 1)`` will only keep the bits 131 | of the hash that are lower than ``nTableSize``, which is the same thing ``h % nTableSize`` does. 132 | 133 | The value ``nTableSize - 1`` is called the table mask and stored in the ``nTableMask`` member. Using a masking operation 134 | instead of a modulus is just a performance optimization. 135 | 136 | The ``nNextFreeElement`` member specifies the next integer key that will be used when you insert an element using 137 | ``$array[] = $value``. It will be one larger than the largest integer key that was ever used in this hashtable. 138 | 139 | You already know the role of the ``pListHead`` and ``pListTail`` pointers (they are the head/tail of the doubly linked 140 | list specifying the order). The ``pInternalPointer`` is used for iteration and points to the "current" bucket. 141 | 142 | When an item is deleted from the hashtable a destruction function can be called for it, which is stored in the 143 | ``pDestructor`` member. For example, if you are storing ``zval *`` items in the hashtable, you will probably want 144 | ``zval_ptr_dtor`` to be called when an element is removed. 145 | 146 | The ``persistent`` flag specified whether the buckets (and their values) should use persistent allocation. For most 147 | use cases this will be ``0`` as the hashtable is not supposed to live longer than one request. The ``bApplyProtection`` 148 | flag specifies whether the hashtable should use recursion protection (defaults to 1). Recursion protection will throw 149 | an error if the recursion depth (stored in ``nApplyCount``) reaches a certain level. The protection is used for 150 | hashtable comparisons and for the ``zend_hash_apply`` functions. 151 | 152 | The last member ``inconsistent`` is only used in debug builds and stores information on the current state of the 153 | hashtable. This is used to throw errors for some incorrect usages of the hashtable, e.g. if you access a hashtable that 154 | is in the process of being destroyed. -------------------------------------------------------------------------------- /Book/hashtables/hash_algorithm.rst: -------------------------------------------------------------------------------- 1 | Hash algoritması ve çakışma 2 | ============================= 3 | 4 | In this final section on hashtables, we'll have a closer look at worst-case collision scenarios and some properties of 5 | the hashing function that PHP employs. While this knowledge is not necessary for the usage of the hashtable APIs it 6 | should give you a better understanding of the hashtable structure and its limitations. 7 | 8 | Analyzing collisions 9 | -------------------- 10 | 11 | In order to simplify collision analysis, let's first write a helper function ``array_collision_info()`` which will 12 | take an array and tell us which keys collide into which index. In order to do so we'll go through the ``arBuckets`` and 13 | for every index create an array that contains some information about all buckets at that index:: 14 | 15 | PHP_FUNCTION(array_collision_info) { 16 | HashTable *hash; 17 | zend_uint i; 18 | 19 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_DC, "h", &hash) == FAILURE) { 20 | return; 21 | } 22 | 23 | array_init(return_value); 24 | 25 | /* Empty hashtables may not yet be initialized */ 26 | if (hash->nNumOfElements == 0) { 27 | return; 28 | } 29 | 30 | for (i = 0; i < hash->nTableSize; ++i) { 31 | /* Create array of elements at this nIndex */ 32 | zval *elements; 33 | Bucket *bucket; 34 | 35 | MAKE_STD_ZVAL(elements); 36 | array_init(elements); 37 | add_next_index_zval(return_value, elements); 38 | 39 | bucket = hash->arBuckets[i]; 40 | while (bucket != NULL) { 41 | zval *element; 42 | 43 | MAKE_STD_ZVAL(element); 44 | array_init(element); 45 | add_next_index_zval(elements, element); 46 | 47 | add_assoc_long(element, "hash", bucket->h); 48 | 49 | if (bucket->nKeyLength == 0) { 50 | add_assoc_long(element, "key", bucket->h); 51 | } else { 52 | add_assoc_stringl( 53 | element, "key", (char *) bucket->arKey, bucket->nKeyLength - 1, 1 54 | ); 55 | } 56 | 57 | { 58 | zval **data = (zval **) bucket->pData; 59 | Z_ADDREF_PP(data); 60 | add_assoc_zval(element, "value", *data); 61 | } 62 | 63 | bucket = bucket->pNext; 64 | } 65 | } 66 | } 67 | 68 | The code is also a nice usage example for the ``add_`` functions from the previous section. Let's try the function out:: 69 | 70 | var_dump(array_collision_info([2 => 0, 5 => 1, 10 => 2])); 71 | 72 | // Output (reformatted a bit): 73 | 74 | array(8) { 75 | [0] => array(0) {} 76 | [1] => array(0) {} 77 | [2] => array(2) { 78 | [0] => array(3) { 79 | ["hash"] => int(10) 80 | ["key"] => int(10) 81 | ["value"] => int(2) 82 | } 83 | [1] => array(3) { 84 | ["hash"] => int(2) 85 | ["key"] => int(2) 86 | ["value"] => int(0) 87 | } 88 | } 89 | [3] => array(0) {} 90 | [4] => array(0) {} 91 | [5] => array(1) { 92 | [0] => array(3) { 93 | ["hash"] => int(5) 94 | ["key"] => int(5) 95 | ["value"] => int(1) 96 | } 97 | } 98 | [6] => array(0) {} 99 | [7] => array(0) {} 100 | } 101 | 102 | There are several things you can see from this output (most of which you should already be aware of): 103 | 104 | * The outer array has 8 elements, even though only 3 were inserted. This is because 8 is the default initial table 105 | size. 106 | * For integers the hash and the key are always the same. 107 | * Even though the hashes are all different, we still have a collision at ``nIndex == 2`` because 2 % 8 is 2, but 108 | 10 % 8 is also 2. 109 | * The linked collision resolution lists contain the elements in reverse order of insertion. (This is the easiest way 110 | to implement it.) 111 | 112 | Index collisions 113 | ---------------- 114 | 115 | The goal now is to create a worst-case collision scenario where *all* hash keys collide. There are two ways to 116 | accomplish this and we'll start with the easier one: Rather than creating collisions in the hash function, we'll 117 | create the collisions in the index (which is the hash modulo the table size). 118 | 119 | For integer keys this is particularly easy, because no real hashing operation is applied to them. The index will simply 120 | be ``key % nTableSize``. Finding collisions for this expression is trivial, e.g. any key that is a multiple of the 121 | table size will collide. If the table size if 8, then the indices will be 0 % 8 = 0, 8 % 8 = 0, 16 % 8 = 0, 24 % 8 = 0, 122 | etc. 123 | 124 | Here is a PHP script demonstrating this scenario: 125 | 126 | .. code-block:: php 127 | 128 | $size = pow(2, 16); // any power of 2 will do 129 | 130 | $startTime = microtime(true); 131 | 132 | // Insert keys [0, $size, 2 * $size, 3 * $size, ..., ($size - 1) * $size] 133 | 134 | $array = array(); 135 | for ($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) { 136 | $array[$key] = 0; 137 | } 138 | 139 | $endTime = microtime(true); 140 | 141 | printf("Inserted %d elements in %.2f seconds\n", $size, $endTime - $startTime); 142 | printf("There are %d collisions at index 0\n", count(array_collision_info($array)[0])); 143 | 144 | This is the output I get (the results will be different for your machine, but should have the same order of magnitude): 145 | 146 | .. code-block:: none 147 | 148 | Inserted 65536 elements in 34.05 seconds 149 | There are 65536 collisions at index 0 150 | 151 | Of course thirty seconds to insert a handful of elements is *very* slow. What happened? As we have constructed a 152 | scenario where all hash keys collide the performance of inserts degenerates from O(1) to O(n): On every insert PHP has 153 | to walk the collision list for the index in order to check whether an element with the same key already exists. Usually 154 | this is not a problem as the collision list contains only one or two buckets. In the degenerate case on the other hand 155 | *all* elements will be in that list. 156 | 157 | As such PHP has to perform n inserts with O(n) time, which gives a total execution time of O(n^2). Thus instead of doing 158 | 2^16 operations about 2^32 will have to be done. 159 | 160 | Hash collisions 161 | --------------- 162 | 163 | Now that we successfully created a worst-case scenario using index collisions, let's do the same using actual hash 164 | collisions. As this is not possible using integer keys, we'll have to take a look at PHP's string hashing function, 165 | which is defined as follows:: 166 | 167 | static inline ulong zend_inline_hash_func(const char *arKey, uint nKeyLength) 168 | { 169 | register ulong hash = 5381; 170 | 171 | /* variant with the hash unrolled eight times */ 172 | for (; nKeyLength >= 8; nKeyLength -= 8) { 173 | hash = ((hash << 5) + hash) + *arKey++; 174 | hash = ((hash << 5) + hash) + *arKey++; 175 | hash = ((hash << 5) + hash) + *arKey++; 176 | hash = ((hash << 5) + hash) + *arKey++; 177 | hash = ((hash << 5) + hash) + *arKey++; 178 | hash = ((hash << 5) + hash) + *arKey++; 179 | hash = ((hash << 5) + hash) + *arKey++; 180 | hash = ((hash << 5) + hash) + *arKey++; 181 | } 182 | switch (nKeyLength) { 183 | case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ 184 | case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ 185 | case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ 186 | case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ 187 | case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ 188 | case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ 189 | case 1: hash = ((hash << 5) + hash) + *arKey++; break; 190 | case 0: break; 191 | EMPTY_SWITCH_DEFAULT_CASE() 192 | } 193 | return hash; 194 | } 195 | 196 | After removing the manual loop-unrolling the function will look like this:: 197 | 198 | static inline ulong zend_inline_hash_func(const char *arKey, uint nKeyLength) 199 | { 200 | register ulong hash = 5381; 201 | 202 | for (uint i = 0; i < nKeyLength; ++i) { 203 | hash = ((hash << 5) + hash) + arKey[i]; 204 | } 205 | 206 | return hash; 207 | } 208 | 209 | The ``hash << 5 + hash`` expression is the same as ``hash * 32 + hash`` or just ``hash * 33``. Using this we can further 210 | simplify the function:: 211 | 212 | static inline ulong zend_inline_hash_func(const char *arKey, uint nKeyLength) 213 | { 214 | register ulong hash = 5381; 215 | 216 | for (uint i = 0; i < nKeyLength; ++i) { 217 | hash = hash * 33 + arKey[i]; 218 | } 219 | 220 | return hash; 221 | } 222 | 223 | This hash function is called *DJBX33A*, which stands for "Daniel J. Bernstein, Times 33 with Addition". It is one of the 224 | simplest (and as such also one of the fastest) string hashing functions there is. 225 | 226 | Thanks to the simplicity of the hash function finding collisions is not hard. We'll start with two-character collisions, 227 | i.e. we are looking for two strings ``ab`` and ``cd``, which have the same hash: 228 | 229 | .. code-block:: none 230 | 231 | hash(ab) = hash(cd) 232 | <=> (5381 * 33 + a) * 33 + b = (5381 * 33 + c) * 33 + d 233 | <=> a * 33 + b = c * 33 + d 234 | <=> c = a + n 235 | d = b - 33 * n 236 | where n is an integer 237 | 238 | This tells us that we can get a collision by taking a two-char string, incrementing the first char by one and 239 | decrementing the second char by 33. Using this technique we can create groups of 8 strings which all collide. Here is 240 | an example of such a collision group: 241 | 242 | .. code-block:: php 243 | 244 | $array = [ 245 | "E" . chr(122) => 0, 246 | "F" . chr(89) => 1, 247 | "G" . chr(56) => 2, 248 | "H" . chr(23) => 3, 249 | "I" . chr(-10) => 4, 250 | "J" . chr(-43) => 5, 251 | "K" . chr(-76) => 6, 252 | "L" . chr(-109) => 7, 253 | ]; 254 | 255 | var_dump(array_collision_info($array)); 256 | 257 | The output shows that indeed all the keys collide with hash ``193456164``:: 258 | 259 | array(8) { 260 | [0] => array(0) {} 261 | [1] => array(0) {} 262 | [2] => array(0) {} 263 | [3] => array(0) {} 264 | [4] => array(8) { 265 | [0] => array(3) { 266 | ["hash"] => int(193456164) 267 | ["key"] => string(2) "L\x93" 268 | ["value"] => int(7) 269 | } 270 | [1] => array(3) { 271 | ["hash"] => int(193456164) 272 | ["key"] => string(2) "K´" 273 | ["value"] => int(6) 274 | } 275 | [2] => array(3) { 276 | ["hash"] => int(193456164) 277 | ["key"] => string(2) "JÕ" 278 | ["value"] => int(5) 279 | } 280 | [3] => array(3) { 281 | ["hash"] => int(193456164) 282 | ["key"] => string(2) "Iö" 283 | ["value"] => int(4) 284 | } 285 | [4] => array(3) { 286 | ["hash"] => int(193456164) 287 | ["key"] => string(2) "H\x17" 288 | ["value"] => int(3) 289 | } 290 | [5] => array(3) { 291 | ["hash"] => int(193456164) 292 | ["key"] => string(2) "G8" 293 | ["value"] => int(2) 294 | } 295 | [6] => array(3) { 296 | ["hash"] => int(193456164) 297 | ["key"] => string(2) "FY" 298 | ["value"] => int(1) 299 | } 300 | [7] => array(3) { 301 | ["hash"] => int(193456164) 302 | ["key"] => string(2) "Ez" 303 | ["value"] => int(0) 304 | } 305 | } 306 | [5] => array(0) {} 307 | [6] => array(0) {} 308 | [7] => array(0) {} 309 | } 310 | 311 | Once we got one collision group, constructing more collisions is even easier. To do so we make use of the following 312 | property of DJBX33A: If two equal-length strings ``$str1`` and ``$str2`` collide, then ``$prefix.$str1.$postfix`` and 313 | ``$prefix.$str2.$postfix`` will collide as well. It's easy to prove that this is indeed true: 314 | 315 | .. code-block:: none 316 | 317 | hash(prefix . str1 . postfix) 318 | = hash(prefix) * 33^a + hash(str1) * 33^b + hash(postfix) 319 | = hash(prefix) * 33^a + hash(str2) * 33^b + hash(postfix) 320 | = hash(prefix . str2 . postfix) 321 | 322 | where a = strlen(str1 . postfix) and b = strlen(postfix) 323 | 324 | Thus, if ``Ez`` and ``FY`` collide, so will ``abcEzefg`` and ``abcFYefg``. This is also the reason why we could ignore 325 | the trailing NUL-byte that is also part of the hash in the previous considerations: It would result in a different hash, 326 | but the collisions would still be present. 327 | 328 | Using this property large sets of collisions can be created by taking a known set of collisions and concatenating them 329 | in every possible way. E.g. if we know that ``Ez`` and ``FY`` collide, then we also know that all of ``EzEzEz``, 330 | ``EzEzFY``, ``EzFYEz``, ``EzFYFY``, ``FYEzEz``, ``FYEzFY``, ``FYFYEz`` and ``FYFYFY`` will collide. With this method we 331 | can create arbitrarily large sets of collisions. -------------------------------------------------------------------------------- /Book/index.rst: -------------------------------------------------------------------------------- 1 | İçindekiler 2 | ================= 3 | 4 | PHP 5 5 | ----- 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | introduction.rst 11 | build_system.rst 12 | 13 | * PHP eklentileri oluşturmak 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | 18 | zvals.rst 19 | 20 | * Fonksiyonları uygulamak 21 | 22 | .. toctree:: 23 | :maxdepth: 2 24 | 25 | hashtables.rst 26 | classes_objects.rst 27 | 28 | PHP 7 29 | ----- 30 | 31 | Bu kısım sadece PHP 7 ile ilgilidir. Henüz geliştirme aşamasındadır. 32 | 33 | .. toctree:: 34 | :maxdepth: 3 35 | 36 | php7/introduction.rst 37 | php7/build_system.rst 38 | php7/internal_types.rst 39 | php7/extensions_design.rst 40 | php7/memory_management.rst 41 | .. 42 | php7/hashtables.rst 43 | php7/classes_objects.rst 44 | php7/prerequisites.rst 45 | php7/php_first_look.rst 46 | php7/managing_memory.rst 47 | php7/creating_ext.rst 48 | php7/functions.rst 49 | php7/resources.rst 50 | php7/streams.rst 51 | php7/constants.rst 52 | php7/ini.rst 53 | php7/sapis.rst 54 | php7/zend_engine.rst 55 | php7/final.rst 56 | 57 | Dizin ve Arama 58 | ================ 59 | 60 | * :ref:`genindex` 61 | * :ref:`search` 62 | -------------------------------------------------------------------------------- /Book/introduction.rst: -------------------------------------------------------------------------------- 1 | Giriş 2 | ============ 3 | 4 | Bu kitap, birkaç PHP geliştiricisinin işbirliği ile PHP'nin iç işleyişinin nasıl çalıştığını anlatmak ve daha iyi 5 | bir doküman elde etmek üzere hazırlanmıştır. 6 | 7 | Bu kitabın 3 temel amacı vardır: 8 | 9 | * PHP'nin iç işleyişini belgelemek ve açıklamak. 10 | * Dilin eklentiler ile nasıl genişletilebileceğini belgelemek ve açıklamak. 11 | * PHP'yi geliştirmek için toplulukla nasıl etkileşim kuracağınızı belgelemek ve açıklamak. 12 | 13 | Bu kitap öncelikle C programlama dilinde tecrübesi olan geliştiricileri hedef almaktadır. Ancak, C programlama 14 | dilini hiç bilmeyen geliştiriciler için de anlaşılabilir olması için, bilgileri özetlemeye çalışacağız. 15 | 16 | .. note:: Giriş bölümlerinden bazıları henüz yazılmadı (özellikle de temel eklenti geliştirme ve fonksiyon tanımlama 17 | ile ilgili olanlar), bu sebeple PHP eklentisi geliştirme konusunda yeniyseniz, geri kalan giriş dersleri 18 | yayınlanana kadar beklemeniz gerekecek ya da bu konuyla ilgili 19 | `diğer kaynaklardan `_ faydalanarak başlayabilirsiniz. 20 | 21 | Bu kitabın reposu şu adreste mevcuttur: GitHub_. Hata bildirimi ve geridönüşlerinizi 22 | şu adresten bildirebilirsiniz: `issue tracker`_. 23 | 24 | .. _GitHub: https://github.com/phpinternalsbook/PHP-Internals-Book 25 | .. _issue tracker: https://github.com/phpinternalsbook/PHP-Internals-Book/issues 26 | -------------------------------------------------------------------------------- /Book/php7/build_system.rst: -------------------------------------------------------------------------------- 1 | PHP yapı sistemini kullanmak 2 | ============================ 3 | 4 | In this chapter we'll explain how to use the PHP build system to compile both itself and additional extensions. This 5 | chapter will not yet be concerned with writing your own autoconf build instructions and only explain how to use the 6 | tooling. 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | build_system/building_php.rst 14 | build_system/building_extensions.rst 15 | -------------------------------------------------------------------------------- /Book/php7/build_system/building_extensions.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: bash 2 | 3 | PHP eklentilerini yapılandırmak 4 | =============================== 5 | 6 | Now that you know how to compile PHP itself, we'll move on to compiling additional extensions. We'll discuss how the 7 | build process works and what different options are available. 8 | 9 | Paylaşılan eklentileri yüklemek 10 | ------------------------------- 11 | 12 | As you already know from the previous section, PHP extensions can be either built statically into the PHP binary, or 13 | compiled into a shared object (``.so``). Static linkage is the default for most of the bundled extensions, whereas 14 | shared objects can be created by explicitly passing ``--enable-EXTNAME=shared`` or ``--with-EXTNAME=shared`` to 15 | ``./configure``. 16 | 17 | While static extensions will always be available, shared extensions need to be loaded using the ``extension`` or 18 | ``zend_extension`` ini options [#]_. Both options take either an absolute path to the ``.so`` file or a path relative to 19 | the ``extension_dir`` setting. 20 | 21 | As an example, consider a PHP build compiled using this configure line:: 22 | 23 | ~/php-src> ./configure --prefix=$HOME/myphp \ 24 | --enable-debug --enable-maintainer-zts \ 25 | --enable-opcache --with-gmp=shared 26 | 27 | In this case both the opcache extension and GMP extension are compiled into shared objects located in the ``modules/`` 28 | directory. You can load both either by changing the ``extension_dir`` or by passing absolute paths:: 29 | 30 | ~/php-src> sapi/cli/php -dzend_extension=`pwd`/modules/opcache.so \ 31 | -dextension=`pwd`/modules/gmp.so 32 | # or 33 | ~/php-src> sapi/cli/php -dextension_dir=`pwd`/modules \ 34 | -dzend_extension=opcache.so -dextension=gmp.so 35 | 36 | During the ``make install`` step both ``.so`` files will be moved into the extension directory of your PHP installation, 37 | which you may find using the ``php-config --extension-dir`` command. For the above build options it will be 38 | ``/home/myuser/myphp/lib/php/extensions/no-debug-non-zts-MODULE_API``. This value will also be the default of the 39 | ``extension_dir`` ini option, so you won't have to specify it explicitly and can load the extensions directly:: 40 | 41 | ~/myphp> bin/php -dzend_extension=opcache.so -dextension=gmp.so 42 | 43 | This leaves us with one question: Which mechanism should you use? Shared objects allow you to have a base PHP binary and 44 | load additional extensions through the php.ini. Distributions make use of this by providing a bare PHP package and 45 | distributing the extensions as separate packages. On the other hand, if you are compiling your own PHP binary, you 46 | likely don't have need for this, because you already know which extensions you need. 47 | 48 | As a rule of thumb, you'll use static linkage for the extensions bundled by PHP itself and use shared extensions for 49 | everything else. The reason is simply that building external extensions as shared objects is easier (or at least less 50 | intrusive), as you will see in a moment. Another benefit is that you can update the extension without rebuilding PHP. 51 | 52 | .. [#] We'll explain the difference between a "normal" extension and a Zend extension later in the book. For now it 53 | suffices to know that Zend extensions are more "low level" (e.g. opcache or xdebug) and hook into the workings of 54 | the Zend Engine itself. 55 | 56 | PECL'den eklenti yüklemek 57 | ------------------------------- 58 | 59 | PECL_, the *PHP Extension Community Library*, offers a large number of extensions for PHP. When extensions are removed 60 | from the main PHP distribution, they usually continue to exist in PECL. Similarly many extensions that are now bundled 61 | with PHP were previously PECL extensions. 62 | 63 | Unless you specified ``--without-pear`` during the configuration stage of your PHP build, ``make install`` will download 64 | and install PECL as a part of PEAR. You will find the ``pecl`` script in the ``$PREFIX/bin`` directory. Installing 65 | extensions is now as simple as running ``pecl install EXTNAME``, e.g.:: 66 | 67 | ~/myphp> bin/pecl install apcu 68 | 69 | This command will download, compile and install the APCu_ extension. The result will be a ``apcu.so`` file in your 70 | extension directory, which can then be loaded by passing the ``extension=apcu.so`` ini option. 71 | 72 | While ``pecl install`` is very handy for the end-user, it is of little interest to extension developers. In the 73 | following, we'll describe two ways to manually build extensions: Either by importing it into the main PHP source tree 74 | (this allows static linkage) or by doing an external build (only shared). 75 | 76 | .. _PECL: http://pecl.php.net 77 | .. _APCu: http://pecl.php.net/package/APCu 78 | 79 | PHP kaynak ağacına eklenti eklemek 80 | ---------------------------------------- 81 | 82 | There is no fundamental difference between a third-party extension and an extension bundled with PHP. As such you can 83 | build an external extension simply by copying it into the PHP source tree and then using the usual build procedure. 84 | We'll demonstrate this using APCu as an example. 85 | 86 | First of all, you'll have to place the source code of the extension into the ``ext/EXTNAME`` directory of your PHP 87 | source tree. If the extension is available via git, this is as simple as cloning the repository from within ``ext/``:: 88 | 89 | ~/php-src/ext> git clone https://github.com/krakjoe/apcu.git 90 | 91 | Alternatively you can also download a source tarball and extract it:: 92 | 93 | /tmp> wget http://pecl.php.net/get/apcu-4.0.2.tgz 94 | /tmp> tar xzf apcu-4.0.2.tgz 95 | /tmp> mkdir ~/php-src/ext/apcu 96 | /tmp> cp -r apcu-4.0.2/. ~/php-src/ext/apcu 97 | 98 | The extension will contain a ``config.m4`` file, which specifies extension-specific build instructions for use by 99 | autoconf. To incorporate them into the ``./configure`` script, you'll have to run ``./buildconf`` again. To ensure that 100 | the configure file is really regenerated, it is recommended to delete it beforehand:: 101 | 102 | ~/php-src> rm configure && ./buildconf --force 103 | 104 | You can now use the ``./config.nice`` script to add APCu to your existing configuration or start over with a completely 105 | new configure line:: 106 | 107 | ~/php-src> ./config.nice --enable-apcu 108 | # or 109 | ~/php-src> ./configure --enable-apcu # --other-options 110 | 111 | Finally run ``make -jN`` to perform the actual build. As we didn't use ``--enable-apcu=shared`` the extension is 112 | statically linked into the PHP binary, i.e. no additional actions are needed to make use of it. Obviously you can also 113 | use ``make install`` to install the resulting binaries. 114 | 115 | ``phpize`` kullanarak eklenti yapılandırmak 116 | ------------------------------------------- 117 | 118 | It is also possible to build extensions separately from PHP by making use of the ``phpize`` script that was already 119 | mentioned in the :ref:`building_php` section. 120 | 121 | ``phpize`` plays a similar role as the ``./buildconf`` script used for PHP builds: First it will import the PHP build 122 | system into your extension by copying files from ``$PREFIX/lib/php/build``. Among these files are ``acinclude.m4`` 123 | (PHP's M4 macros), ``phpize.m4`` (which will be renamed to ``configure.in`` in your extension and contains the main 124 | build instructions) and ``run-tests.php``. 125 | 126 | Then ``phpize`` will invoke autoconf to generate a ``./configure`` file, which can be used to customize the extension 127 | build. Note that it is not necessary to pass ``--enable-apcu`` to it, as this is implicitly assumed. Instead you should 128 | use ``--with-php-config`` to specify the path to your ``php-config`` script:: 129 | 130 | /tmp/apcu-4.0.2> ~/myphp/bin/phpize 131 | Configuring for: 132 | PHP Api Version: 20121113 133 | Zend Module Api No: 20121113 134 | Zend Extension Api No: 220121113 135 | 136 | /tmp/apcu-4.0.2> ./configure --with-php-config=$HOME/myphp/bin/php-config 137 | /tmp/apcu-4.0.2> make -jN && make install 138 | 139 | You should always specify the ``--with-php-config`` option when building extensions (unless you have only a single, 140 | global installation of PHP), otherwise ``./configure`` will not be able to correctly determine what PHP version and 141 | flags to build against. Specifying the ``php-config`` script also ensures that ``make install`` will move the generated 142 | ``.so`` file (which can be found in the ``modules/`` directory) to the right extension directory. 143 | 144 | As the ``run-tests.php`` file was also copied during the ``phpize`` stage, you can run the extension tests using 145 | ``make test`` (or an explicit call to run-tests). 146 | 147 | The ``make clean`` target for removing compiled objects is also available and allows you to force a full rebuild of 148 | the extension, should the incremental build fail after a change. Additionally phpize provides a cleaning option via 149 | ``phpize --clean``. This will remove all the files imported by ``phpize``, as well as the files generated by the 150 | ``/configure`` script. 151 | 152 | Eklentiler hakkındaki bilgileri görüntülemek 153 | -------------------------------------------- 154 | 155 | The PHP CLI binary provides several options to display information about extensions. You already know ``-m``, which will 156 | list all loaded extensions. You can use it to verify that an extension was loaded correctly:: 157 | 158 | ~/myphp/bin> ./php -dextension=apcu.so -m | grep apcu 159 | apcu 160 | 161 | There are several further switches beginning with ``--r`` that expose Reflection functionality. For example you can use 162 | ``--ri`` to display the configuration of an extension:: 163 | 164 | ~/myphp/bin> ./php -dextension=apcu.so --ri apcu 165 | apcu 166 | 167 | APCu Support => disabled 168 | Version => 4.0.2 169 | APCu Debugging => Disabled 170 | MMAP Support => Enabled 171 | MMAP File Mask => 172 | Serialization Support => broken 173 | Revision => $Revision: 328290 $ 174 | Build Date => Jan 1 2014 16:40:00 175 | 176 | Directive => Local Value => Master Value 177 | apc.enabled => On => On 178 | apc.shm_segments => 1 => 1 179 | apc.shm_size => 32M => 32M 180 | apc.entries_hint => 4096 => 4096 181 | apc.gc_ttl => 3600 => 3600 182 | apc.ttl => 0 => 0 183 | # ... 184 | 185 | The ``--re`` switch lists all ini settings, constants, functions and classes added by an extension: 186 | 187 | .. code-block:: none 188 | 189 | ~/myphp/bin> ./php -dextension=apcu.so --re apcu 190 | Extension [ extension #27 apcu version 4.0.2 ] { 191 | - INI { 192 | Entry [ apc.enabled ] 193 | Current = '1' 194 | } 195 | Entry [ apc.shm_segments ] 196 | Current = '1' 197 | } 198 | # ... 199 | } 200 | 201 | - Constants [1] { 202 | Constant [ boolean APCU_APC_FULL_BC ] { 1 } 203 | } 204 | 205 | - Functions { 206 | Function [ function apcu_cache_info ] { 207 | 208 | - Parameters [2] { 209 | Parameter #0 [ $type ] 210 | Parameter #1 [ $limited ] 211 | } 212 | } 213 | # ... 214 | } 215 | } 216 | 217 | The ``--re`` switch only works for normal extensions, Zend extensions use ``--rz`` instead. You can try this on 218 | opcache:: 219 | 220 | ~/myphp/bin> ./php -dzend_extension=opcache.so --rz "Zend OPcache" 221 | Zend Extension [ Zend OPcache 7.0.3-dev Copyright (c) 1999-2013 by Zend Technologies ] 222 | 223 | As you can see, this doesn't display any useful information. The reason is that opcache registers both a normal 224 | extension and a Zend extension, where the former contains all ini settings, constants and functions. So in this 225 | particular case you still need to use ``--re``. Other Zend extensions make their information available via ``--rz`` 226 | though. 227 | 228 | Eklentilerin API uyumluluğu 229 | **************************** 230 | 231 | Extensions are very sensitive to 5 major factors. If they dont fit, the extension wont load into PHP and will be 232 | useless: 233 | 234 | * PHP Api Version 235 | * Zend Module Api No 236 | * Zend Extension Api No 237 | * Debug mode 238 | * Thread safety 239 | 240 | The *phpize* tool recall you some of those informations. 241 | So if you have built a PHP with debug mode, and try to make it load and use an extension which's been built without 242 | debug mode, it simply wont work. Same for the other checks. 243 | 244 | *PHP Api Version* is the number of the version of the internal API. *Zend Module Api No* and *Zend Extension Api No* 245 | are respectively about PHP extensions and Zend extensions API. 246 | 247 | Those numbers are later passed as C macros to the extension beeing built, so that it can itself check against those 248 | parameters and take different code paths based on C preprocessor ``#ifdef``\s. As those numbers are passed to the 249 | extension code as macros, they are written in the extension structure, so that anytime you try to load this extension in 250 | a PHP binary, they will be checked against the PHP binary's own numbers. 251 | If they mismatch, then the extension will not load, and an error message will be displayed. 252 | 253 | If we look at the extension C structure, it looks like this:: 254 | 255 | zend_module_entry foo_module_entry = { 256 | STANDARD_MODULE_HEADER, 257 | "foo", 258 | foo_functions, 259 | PHP_MINIT(foo), 260 | PHP_MSHUTDOWN(foo), 261 | NULL, 262 | NULL, 263 | PHP_MINFO(foo), 264 | PHP_FOO_VERSION, 265 | STANDARD_MODULE_PROPERTIES 266 | }; 267 | 268 | What is interesting for us so far, is the ``STANDARD_MODULE_HEADER`` macro. If we expand it, we can see:: 269 | 270 | #define STANDARD_MODULE_HEADER_EX sizeof(zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS 271 | #define STANDARD_MODULE_HEADER STANDARD_MODULE_HEADER_EX, NULL, NULL 272 | 273 | Notice how ``ZEND_MODULE_API_NO``, ``ZEND_DEBUG``, ``USING_ZTS`` are used. 274 | 275 | 276 | If you look at the default directory for PHP extensions, it should look like ``no-debug-non-zts-20090626``. As you'd 277 | have guessed, this directory is made of distinct parts joined together : debug mode, followed by thread safety 278 | information, followed by the Zend Module Api No. 279 | So by default, PHP tries to help you navigating with extensions. 280 | 281 | .. note:: 282 | 283 | Usually, when you become an internal developper or an extension developper, you will have to play with 284 | the debug parameter, and if you have to deal with the Windows platform, threads will show up as well. You can 285 | end with compiling the same extension several times against several cases of those parameters. 286 | 287 | Remember that every new major/minor version of PHP change parameters such as the PHP Api Version, that's why you need 288 | to recompile extensions against a newer PHP version. 289 | 290 | .. code-block:: none 291 | 292 | > /path/to/php70/bin/phpize -v 293 | Configuring for: 294 | PHP Api Version: 20151012 295 | Zend Module Api No: 20151012 296 | Zend Extension Api No: 320151012 297 | 298 | > /path/to/php71/bin/phpize -v 299 | Configuring for: 300 | PHP Api Version: 20160303 301 | Zend Module Api No: 20160303 302 | Zend Extension Api No: 320160303 303 | 304 | > /path/to/php56/bin/phpize -v 305 | Configuring for: 306 | PHP Api Version: 20131106 307 | Zend Module Api No: 20131226 308 | Zend Extension Api No: 220131226 309 | 310 | .. note:: 311 | 312 | *Zend Module Api No* is itself built with a date using the *year.month.day* format. This is the date of the day the 313 | API changed and was tagged. 314 | *Zend Extension Api No* is the Zend version followed by *Zend Module Api No*. 315 | 316 | .. note:: 317 | 318 | Too many numbers? Yes. One API number, bound to one PHP version, would really be enough for anybody and would ease 319 | the understanding of PHP versionning. Unfortunately, we got 3 different API numbers in addition to the PHP version 320 | itself. Which one should you look for ? The answer is any : they all-three-of-them evolve when PHP version evolve. 321 | For historical reasons, we got 3 different numbers. 322 | 323 | But, you are a C developper anren't you ? Why not build a "compatibility" header on your side, based on such number ? 324 | We authors, use something like this in extensions of ours:: 325 | 326 | #include "php.h" 327 | #include "Zend/zend_extensions.h" 328 | 329 | #define PHP_5_5_X_API_NO 220121212 330 | #define PHP_5_6_X_API_NO 220131226 331 | 332 | #define PHP_7_0_X_API_NO 320151012 333 | #define PHP_7_1_X_API_NO 320160303 334 | #define PHP_7_2_X_API_NO 320160731 335 | 336 | #define IS_PHP_72 ZEND_EXTENSION_API_NO == PHP_7_2_X_API_NO 337 | #define IS_AT_LEAST_PHP_72 ZEND_EXTENSION_API_NO >= PHP_7_2_X_API_NO 338 | 339 | #define IS_PHP_71 ZEND_EXTENSION_API_NO == PHP_7_1_X_API_NO 340 | #define IS_AT_LEAST_PHP_71 ZEND_EXTENSION_API_NO >= PHP_7_1_X_API_NO 341 | 342 | #define IS_PHP_70 ZEND_EXTENSION_API_NO == PHP_7_0_X_API_NO 343 | #define IS_AT_LEAST_PHP_70 ZEND_EXTENSION_API_NO >= PHP_7_0_X_API_NO 344 | 345 | #define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO 346 | #define IS_AT_LEAST_PHP_56 (ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO && ZEND_EXTENSION_API_NO < PHP_7_0_X_API_NO) 347 | 348 | #define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO 349 | #define IS_AT_LEAST_PHP_55 (ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO && ZEND_EXTENSION_API_NO < PHP_7_0_X_API_NO) 350 | 351 | #if ZEND_EXTENSION_API_NO >= PHP_7_0_X_API_NO 352 | #define IS_PHP_7 1 353 | #define IS_PHP_5 0 354 | #else 355 | #define IS_PHP_7 0 356 | #define IS_PHP_5 1 357 | #endif 358 | 359 | See ? 360 | -------------------------------------------------------------------------------- /Book/php7/extensions_design.rst: -------------------------------------------------------------------------------- 1 | Eklentilerin dizaynı 2 | ==================== 3 | 4 | In this chapter you'll learn how to design PHP extensions. You'll learn about the PHP lifecycle, how and when to manage 5 | memory, the different hooks you can use or the different function pointers you can replace to actively change PHP's 6 | internal machinery. You'll design PHP functions and classes to provide them through your extension, and you'll play 7 | with INI settings. 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | extensions_design/php_lifecycle.rst 15 | extensions_design/globals_management.rst 16 | -------------------------------------------------------------------------------- /Book/php7/extensions_design/globals_management.rst: -------------------------------------------------------------------------------- 1 | Global alanı yönetmek 2 | ===================== 3 | 4 | 5 | -------------------------------------------------------------------------------- /Book/php7/extensions_design/images/php_classic_lifetime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IstanbulPHP/PHP-Internals-Book/b62941ec684a34e7548e1cbbba8a26cdc7cee605/Book/php7/extensions_design/images/php_classic_lifetime.png -------------------------------------------------------------------------------- /Book/php7/extensions_design/images/php_extensions_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IstanbulPHP/PHP-Internals-Book/b62941ec684a34e7548e1cbbba8a26cdc7cee605/Book/php7/extensions_design/images/php_extensions_lifecycle.png -------------------------------------------------------------------------------- /Book/php7/extensions_design/images/php_lifetime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IstanbulPHP/PHP-Internals-Book/b62941ec684a34e7548e1cbbba8a26cdc7cee605/Book/php7/extensions_design/images/php_lifetime.png -------------------------------------------------------------------------------- /Book/php7/extensions_design/images/php_lifetime_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IstanbulPHP/PHP-Internals-Book/b62941ec684a34e7548e1cbbba8a26cdc7cee605/Book/php7/extensions_design/images/php_lifetime_process.png -------------------------------------------------------------------------------- /Book/php7/extensions_design/images/php_lifetime_thread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IstanbulPHP/PHP-Internals-Book/b62941ec684a34e7548e1cbbba8a26cdc7cee605/Book/php7/extensions_design/images/php_lifetime_thread.png -------------------------------------------------------------------------------- /Book/php7/extensions_design/php_lifecycle.rst: -------------------------------------------------------------------------------- 1 | PHP hayat döngüsünü öğrenmek 2 | ============================ 3 | 4 | PHP's a complex machinery which lifecycle should really be mastered by anyone pretending to hook into it. 5 | The main line is as follow : 6 | 7 | PHP starts up. If running CLI or FPM, it's C ``main()`` is run. If running as a module into a webserver, like using the 8 | apxs2 SAPI (Apache 2), PHP is started up just a little bit after Apache itself starts up and comes to run the startup 9 | sequence of its module which PHP is one. Starting up, is called internally **the module startup step**. We also 10 | abbreviate it as the **MINIT** step. 11 | 12 | Once started, PHP waits to treat one/several requests. When we talk about PHP CLI, there will be only one request: the 13 | current script to run. However, when we talk about a web environment- should it be PHP-FPM or webserver 14 | module- PHP could serve several requests one after the other. It all depends on how you did configure you webserver: 15 | you may tell it to serve an infinite number of requests, or a specific number before shutting down and recycling the 16 | process. Everytime a new request shows in to be treated, PHP will run **a request startup step**. We call it the 17 | **RINIT**. 18 | 19 | The request is served, some content is (probably) generated, OK. Time to shutdown the request and get prepared to 20 | eventually treat another one. Shutting down a request is called **the request shutdown step**. 21 | 22 | After having treated X requests (one, several dozens, thousands etc..), PHP will finally shut down itself, and die. 23 | Shutting down the PHP process is called **the module shutdown step**. We abbreviate it as **MSHUTDOWN**. 24 | 25 | If we would have drawn those steps, that could give something like this: 26 | 27 | .. image:: ./images/php_classic_lifetime.png 28 | :align: center 29 | 30 | Paralellik modelleri 31 | ********************** 32 | 33 | In a CLI environment, everything is easy : one PHP process will treat one request : it will launch one solo PHP script, 34 | then die. 35 | The CLI environment is a specialization of the Web environment, which is more complex. 36 | 37 | To treat several requests at the same time, you need to run a parallelism model. There exists two of them in PHP: 38 | 39 | * The process-based model 40 | * The thread-based model 41 | 42 | Using the process-based model, every PHP interpreter is isolated by the OS into its own process. 43 | This model is very common under Unix. Every request leaves into its own process. 44 | This model is used by PHP-CLI, PHP-FPM and PHP-CGI. 45 | 46 | With the thread-based model, every PHP interpreter is isolated into a thread, using a thread library. 47 | This model is mainly used under Microsoft Windows OS, but can be used with most Unixes as well. This requires PHP and 48 | its extensions :doc:`to be built <../build_system/building_extensions>` in ZTS mode. 49 | 50 | Here is the process-based model: 51 | 52 | .. image:: ./images/php_lifetime_process.png 53 | :align: center 54 | 55 | And here is the thread-based model: 56 | 57 | .. image:: ./images/php_lifetime_thread.png 58 | :align: center 59 | 60 | .. note:: PHP's multi-processing module is not of your choice, as an extension developer. You will have to support it. 61 | You will have to support the fact that your extension could run in a threaded environment, especially under 62 | Windows platforms, and you'll have to program against it. 63 | 64 | PHP eklenti hookları 65 | ******************** 66 | 67 | As you could have guessed, the PHP engine will trigger your extension at several lifetime points. We call those *hook 68 | functions*. Your extension may declare interest into specific lifetime points by declaring function hooks while it 69 | registers against the engine. 70 | 71 | Those hooks can clearly be noticed once you analyze a PHP extension structure, the ``zend_module_entry`` structure:: 72 | 73 | struct _zend_module_entry { 74 | unsigned short size; 75 | unsigned int zend_api; 76 | unsigned char zend_debug; 77 | unsigned char zts; 78 | const struct _zend_ini_entry *ini_entry; 79 | const struct _zend_module_dep *deps; 80 | const char *name; 81 | const struct _zend_function_entry *functions; 82 | int (*module_startup_func)(INIT_FUNC_ARGS); /* MINIT() */ 83 | int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* MSHUTDOWN() */ 84 | int (*request_startup_func)(INIT_FUNC_ARGS); /* RINIT() */ 85 | int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* RSHUTDOWN() */ 86 | void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); /* PHPINFO() */ 87 | const char *version; 88 | size_t globals_size; 89 | #ifdef ZTS 90 | ts_rsrc_id* globals_id_ptr; 91 | #else 92 | void* globals_ptr; 93 | #endif 94 | void (*globals_ctor)(void *global); /* GINIT() */ 95 | void (*globals_dtor)(void *global); /* GSHUTDOWN */ 96 | int (*post_deactivate_func)(void); /* PRSHUTDOWN() */ 97 | int module_started; 98 | unsigned char type; 99 | void *handle; 100 | int module_number; 101 | const char *build_id; 102 | }; 103 | 104 | Let's now have a look at what kind of code you should write in any of those hooks. 105 | 106 | Module initialization: MINIT() 107 | ------------------------------ 108 | 109 | This is PHP process startup step. In your extension's ``MINIT()``, you'll load and allocate any persistent object or 110 | piece of information you'll need for every future request. 111 | For the big part of them, those allocations will target read-only objects. 112 | 113 | In ``MINIT()``, no thread or process has popped yet, so you may fully access global variables with no protection at 114 | all. Also, you must not allocate memory that is request-bound, as a request has not started yet. 115 | You never use :doc:`Zend Memory Manager <../memory_management/zend_memory_manager>` allocations in ``MINIT()`` steps, 116 | but persistent allocations. No ``emalloc()``, but ``pemalloc()``. Failing to do that will lead to crashes. 117 | 118 | At ``MINIT()``, the execution engine is not started yet, so beware of not trying to access any of its structure without 119 | special care. 120 | 121 | If you need to register INI entries for your extension, ``MINIT()`` is the right step to do that. 122 | 123 | If you need to register read-only ``zend_strings`` for further usage, it is time to do so here (with persistent alloc). 124 | 125 | If you need to allocate objects that well be written to while serving a request, then you'll need to duplicate their 126 | memory allocation to a thread-specific pool for the request. Remember that you can only write safely to global space 127 | while into ``MINIT()``. 128 | 129 | .. note:: Memory management, allocations, and debugging; are part of the :doc:`memory management<../memory_management>` 130 | chapter. 131 | 132 | ``MINIT()`` is triggered by ``zend_startup_modules()`` in 133 | `php_module_startup() `_ 134 | function. 135 | 136 | Module termination: MSHUTDOWN() 137 | ------------------------------- 138 | 139 | This is PHP process shutdown step. Easy enough, you basically perform here the exact opposite of what you used in 140 | ``MINIT()``. You free your resources, you unregister your INI settings, etc.. 141 | 142 | Take care again here: the execution engine is shut down, so you should not access any of its variable (but you should 143 | not need to here). 144 | 145 | As you don't live in a request here, you should not free resources using 146 | :doc:`Zend Memory Manager <../memory_management/zend_memory_manager>` ``efree()`` or alikes, but free for persistent 147 | allocations, aka ``pefree()``. 148 | 149 | ``MSHUTDOWN()`` is triggered by ``zend_destroy_modules()`` from ``zend_shutdown()`` in 150 | `php_module_shutdown() `_ 151 | function. 152 | 153 | Request initialization: RINIT() 154 | ------------------------------- 155 | 156 | A request just showed in, and PHP is about to treat it here. In ``RINIT()``, you bootstrap the resources you need to 157 | treat that precise request. PHP is a share-nothing architecture, and as-is, it provides 158 | :doc:`memory management <../memory_management>` facilities. 159 | 160 | In ``RINIT()``, if you need to allocate dynamic memory, you'll use Zend Memory Manager. You will call for ``emalloc()``. 161 | Zend Memory Manager tracks the memory you allocate through it, and when the request shuts down, it will attempt to free 162 | the request-bound memory if you forgot to do so (you should not). 163 | 164 | You should not require persistent dynamic memory here, aka libc's ``malloc()`` or Zend's ``pemalloc()``. If you require 165 | persistent memory here, and forgets to free it, you'll create leaks that will stack as PHP treats more and more 166 | requests, to finally crash the process (Kernel OOM) and starve the machine memory. 167 | 168 | Also, take really care not to write to global space here. If PHP is run into a thread as chosen parallelism model, then 169 | you'll modify the context for every thread of the pool (every other request treated in parallel to yours) and you could 170 | also trigger race conditions if you don't lock the memory. 171 | If you need globals, you'll need to protect them. 172 | 173 | .. note:: Global scope management is explained into :doc:`a dedicated chapter `. 174 | 175 | ``RINIT()`` is triggered by ``zend_activate_module()`` in 176 | `php_request_startup() `_ 177 | function. 178 | 179 | Request termination: RSHUTDOWN() 180 | -------------------------------- 181 | 182 | This is PHP request shutdown step. PHP just finished treating its request, and now it cleans up part of its memory as 183 | the share-nothing architecture. Further request to come should not remember anything from the current request. 184 | Easy enough, you basically perform here the exact opposite of what you used in ``RINIT()``. You free your request-bound 185 | resources. 186 | 187 | As you live in a request here, you should free resources using Zend Memory Manager ``efree()`` or alikes. 188 | If you forget to free and leak, on debug builds, the memory manager will push on process *stderr* a log about the 189 | pointers you are leaking, and it will free them for you. 190 | 191 | To give you an idea, ``RSHUTDOWN()`` is called: 192 | 193 | * After userland shutdown functions have been executed (``register_shutdown_function()``) 194 | * After every object destructor have been called 195 | * After PHP output buffer has been flushed 196 | * After max_execution_time has been disabled 197 | 198 | ``RSHUTDOWN()`` is triggered by ``zend_deactivate_modules()`` in 199 | `php_request_shutdown() `_ 200 | function. 201 | 202 | Request Post termination: PRSHUTDOWN() 203 | -------------------------------------- 204 | 205 | This hook is rarely used. It is called after ``RSHUTDOWN()``, but some additionnal engine code hash run in-between. 206 | 207 | Especially, in Post-RSHUTDOWN: 208 | 209 | * The PHP output buffer has been closed and its handlers flushed 210 | * PHP super-globals have been destroyed 211 | * The execution engine has been shut down 212 | 213 | This hook is very rare to use. It is triggered a little bit after ``RSHUTDOWN()``, by ``zend_deactivate_modules()`` in 214 | `php_request_shutdown() `_ 215 | function. 216 | 217 | Globals initialization: GINIT() 218 | ------------------------------- 219 | 220 | This hook is called everytime a thread is popped by the Threading library. If you use processes as multi-processing 221 | facility, this function is called only once, while PHP is starting up, just before ``MINIT()`` gets triggered. 222 | 223 | Not providing too many details here, you simply should initialize your globals here, usually to the value zero. 224 | Globals management will be covered in its dedicated chapter. 225 | 226 | Remember that globals are not cleared after every request. If you need to reset them for every new request (likely), 227 | then you need to put such a procedure into ``RINIT()``. 228 | 229 | .. note:: Global scope management is explained into :doc:`a dedicated chapter `. 230 | 231 | Globals termination: GSHUTDOWN() 232 | -------------------------------- 233 | 234 | This hook is called everytime a thread dies from the Threading library. If you use processes as multi-processing 235 | facility, this function is called only once, as part of PHP shutdown (during ``MSHUTDOWN()``). 236 | 237 | Not providing too many details here, you simply should de-initialize your globals here, usually you have nothing to do, 238 | but if you allocated resources while constructing globals (``GINIT()``), here is the step where you should release them. 239 | 240 | Globals management will be covered in its dedicated chapter. 241 | 242 | Remember that globals are not cleared after every request; aka ``GSHUTDOWN()`` is not called as part of ``RSHUTDOWN()``. 243 | 244 | .. note:: Global scope management is explained into :doc:`a dedicated chapter `. 245 | 246 | Thoughts on PHP lifecycle 247 | ------------------------- 248 | .. image:: ./images/php_extensions_lifecycle.png 249 | :align: center 250 | 251 | As you may have spotted, ``RINIT()`` and ``RSHUTDOWN()`` are especially crucial as they could get triggered thousands 252 | of times on your extension. If the PHP setup is about Web (not CLI), and has been configured so that it can treat an 253 | infinite number of requests, thus your RINIT()/RSHUTDOWN() couple will be called an infinite amount of time. 254 | 255 | We'd like to once more get your attention about memory management. The little tiny byte you'll eventually leak while 256 | treating a request (between ``RINIT()`` and ``RSHUTDOWN()``) will have dramatic consequences on fully loaded servers. 257 | 258 | Also, if you crash with a SIGSEGV signal (bad memory access), you crash the entire process. If the PHP setup was using 259 | threads as multi-processing engine, then you crash every other thread with you, and could even crash the webserver. 260 | 261 | .. note:: The C language is not the PHP language. Using C, errors and mistakes in your program will highly probably 262 | lead to a program crash and termination. 263 | 264 | .. todo: Add a chapter about debugging. Add a chapter about memory leak tracking. 265 | 266 | Fonksiyon işaretçilerini üzerine yazarak hooklamak 267 | ************************************************** 268 | 269 | Now you know when the engine will trigger your code, there exists also noticeable function pointers you may replace to 270 | hook into the engine. 271 | As those pointers are global variables, you may replace them into ``MINIT()`` step, and put them back into 272 | ``MSHUTDOWN()``. 273 | 274 | Those of interest are: 275 | 276 | .. +---------+-----------------+---------------------------------------------------------------------------+ 277 | .. | Subject | Definition file | function | 278 | .. +---------+-----------------+---------------------------------------------------------------------------+ 279 | .. | Error | Zend/zend.h | `void (*zend_error_cb)(int type, const char *error_filename, | 280 | .. | | | const uint error_lineno, const char *format, va_list args)` | 281 | .. +---------+-----------------+---------------------------------------------------------------------------+ 282 | .. | | | | 283 | .. +---------+-----------------+---------------------------------------------------------------------------+ 284 | 285 | * AST, Zend/zend_ast.h: 286 | * `void (*zend_ast_process_t)(zend_ast *ast)` 287 | 288 | * Compiler, Zend/zend_compile.h: 289 | * `zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type)` 290 | * `zend_op_array *(*zend_compile_string)(zval *source_string, char *filename)` 291 | 292 | * Executor, Zend/zend_execute.h: 293 | * `void (*zend_execute_ex)(zend_execute_data *execute_data)` 294 | * `void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value)` 295 | 296 | * GC, Zend/zend_gc.h: 297 | * `int (*gc_collect_cycles)(void)` 298 | 299 | * TSRM, TSRM/TSRM.h: 300 | * `void (*tsrm_thread_begin_func_t)(THREAD_T thread_id)` 301 | * `void (*tsrm_thread_end_func_t)(THREAD_T thread_id)` 302 | 303 | * Error, Zend/zend.h: 304 | * `void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, 305 | va_list args)` 306 | 307 | * Exceptions, Zend/zend_exceptions.h: 308 | * `void (*zend_throw_exception_hook)(zval *ex)` 309 | 310 | * Lifetime, Zend/zend.h: 311 | * `void (*zend_on_timeout)(int seconds)` 312 | * `void (*zend_interrupt_function)(zend_execute_data *execute_data)` 313 | * `void (*zend_ticks_function)(int ticks)` 314 | 315 | Other exists but the above ones are the most important ones you could need while designing PHP extensions. 316 | As their names are self explanatory, there is no need to detail every of them. 317 | 318 | If you need some more informations, you can look for them into PHP source code, and discover when and how they get 319 | triggered. 320 | -------------------------------------------------------------------------------- /Book/php7/internal_types.rst: -------------------------------------------------------------------------------- 1 | Dahili tipler 2 | ============== 3 | 4 | In this chapter we will detail the special types used internally by PHP. Some of those types are directly bound to 5 | userland PHP, like the "zval" data structure. Other structures/types, like the "zend_string" one, is not really visible 6 | from userland point of view, but is a detail to know if you plan to program PHP from inside. 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | internal_types/zvals.rst 14 | internal_types/strings.rst 15 | internal_types/zend_resources.rst 16 | internal_types/hashtables.rst 17 | .. 18 | internal_types/objects/classes_and_objects.rst 19 | -------------------------------------------------------------------------------- /Book/php7/internal_types/hashtables.rst: -------------------------------------------------------------------------------- 1 | Hash tabloları 2 | ============== 3 | 4 | 5 | -------------------------------------------------------------------------------- /Book/php7/internal_types/strings.rst: -------------------------------------------------------------------------------- 1 | String yönetimi 2 | ================== 3 | 4 | Any program needs to manage strings. Managing is like allocating, searching, concatenating, extending, shrinking etc.. 5 | 6 | Many operations are needed with strings. Although the C standard library provides many functions for such a goal, 7 | C classical strings, aka ``char *`` (or ``char []``) are usually a little bit weak to use as-is in a strong program 8 | like PHP is. 9 | 10 | Thus, PHP designed a layer on top of C strings : ``zend_strings``. Also, anoter API exists that implements common string 11 | operations both for C classical strings, or for ``zend_strings``: ``smart_str`` API. 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | strings/zend_strings.rst 17 | strings/smart_str.rst 18 | -------------------------------------------------------------------------------- /Book/php7/internal_types/strings/images/zend_string_memory_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IstanbulPHP/PHP-Internals-Book/b62941ec684a34e7548e1cbbba8a26cdc7cee605/Book/php7/internal_types/strings/images/zend_string_memory_layout.png -------------------------------------------------------------------------------- /Book/php7/internal_types/strings/smart_str.rst: -------------------------------------------------------------------------------- 1 | smart_str API 2 | ============= 3 | 4 | 5 | -------------------------------------------------------------------------------- /Book/php7/internal_types/zend_resources.rst: -------------------------------------------------------------------------------- 1 | Bir kaynak tipi: zend_resource 2 | ================================ 3 | 4 | Even though PHP could really get rid of the "resource" type, because 5 | :doc:`custom object storage <./classes_objects/custom_object_storage>` allows to build a PHP representation of any 6 | abstract kind of data, that resource type still exists in PHP, and you may need to deal with it. 7 | 8 | If you need to create resources, we really would like to push you not to, but instead use objects and their 9 | :doc:`custom storage <./classes_objects/custom_object_storage>` management. Objects is the PHP type that can embed 10 | anything of any type. However, for historical reasons, PHP still knows about that special type "Resource", and still 11 | makes use of it in its heart or in some extensions. Let's see that type together. Beware however, it is really cryptic 12 | and suffers from a long past history, so don't be suprised about its design especially when reading the source code 13 | about it 14 | 15 | "Kaynak" nedir? 16 | ----------------------------- 17 | 18 | Easy enough you know about it. We are talking about this here: 19 | 20 | .. code-block:: php 21 | 22 | $fp = fopen('/proc/cpuinfo', 'r'); 23 | var_dump($fp); /* resource(2) of type (stream) */ 24 | 25 | Internally, a resource is bound to the ``zend_resource`` structure type:: 26 | 27 | struct _zend_resource { 28 | zend_refcounted_h gc; 29 | int handle; 30 | int type; 31 | void *ptr; 32 | }; 33 | 34 | We find the traditionnal ``zend_refcounted_h`` header, meaning that resources are reference countable. If you feel lost 35 | with reference counting and memory tracking, you may refer to the :doc:`./zvals/memory_management` chapter. 36 | 37 | The ``handle`` is an integer that is used internally by the engine to locate the resource into an internal resource 38 | table. It is used as the key for such a table. 39 | 40 | The ``type`` is used to regroup resources of the same type together. This is about the way resources get destroyed and 41 | how they are fetched back from their handle. 42 | 43 | Finally, the ``ptr`` field in ``zend_resource`` is your abstract data. Remember resources are about storing an abstract 44 | data that cannot fit in any data type PHP can represent natively. 45 | 46 | Kaynak tipleri ve kaynak yok etme 47 | --------------------------------------- 48 | 49 | Resources must register a destructor. When users use resources in PHP userland, they usually don't bother cleaning 50 | those when they don't make use of them anymore. For example, it is not uncommon to see an ``fopen()`` call, and not see 51 | the ``fclose()`` call. Using the C language, this would be at best a bad idea, at most a disaster. But using a high 52 | level language like PHP, you ease things. 53 | 54 | You, as an internal developer, must be prepared to the fact that the user would create a lot of resources you'll allow 55 | him to use, without properly cleaning them and releasing memory/OS resource. You hence must register a destructor that 56 | will be called anytime the engine is about to destroy a resource of that type. 57 | 58 | Destructors are grouped by types, so are resources themselves. You won't apply the destructor for a resource of type 59 | 'database' than for a resource of type 'file'. 60 | 61 | There also exists two kinds of resources, here again differenciated about their lifetime. 62 | 63 | * Classical resources, the most used ones, do not persist across several requests, their destructor is called at 64 | request shutdown 65 | * Persistent resources will persist across several requests and will only get destroyed when the PHP process dies. 66 | 67 | Kaynaklarla oynamak 68 | ---------------------- 69 | 70 | The resources related API can be found in 71 | `zend/zend_list.c `_. 72 | You may find some inconsistencies into it, like talking about "lists" for "resources". 73 | 74 | Creating resources 75 | ****************** 76 | 77 | To create a resource, one must first register a destructor for it and associate it to a resource type name using 78 | ``zend_register_list_destructors_ex()``. That call will return an integer that represents the type of resource you 79 | register. You must remember that integer because you will need it later-on to fetch back your resource from the user. 80 | 81 | After that, you can register a new resource using ``zend_register_resource()``. That one will return you a 82 | ``zend_resource``. Let's see together a simple use-case example:: 83 | 84 | #include 85 | 86 | int res_num; 87 | FILE *fp; 88 | zend_resource *my_res; 89 | zval my_val; 90 | 91 | static void my_res_dtor(zend_resource *rsrc) 92 | { 93 | fclose((FILE *)rsrc->ptr); 94 | } 95 | 96 | /* module_number should be your PHP extension number here */ 97 | res_num = zend_register_list_destructors_ex(my_res_dtor, NULL, "my_res", module_number); 98 | fp = fopen('/proc/cpuinfo', "r"); 99 | my_res = zend_register_resource((void *)fp, res_num); 100 | 101 | ZVAL_RES(&my_val, my_res); 102 | 103 | What we do in the code above, is that we open a file using libc's ``fopen()``, and store the returned pointer into a 104 | resource. Before that, we registered a destructor which will use libc's ``fclose()`` on the pointer. Then, we register 105 | the resource against the engine, and we pass the resource into a ``zval`` container that could get returned to userland. 106 | 107 | .. note:: Zvals chapter can be found :doc:`here <./zvals>`. 108 | 109 | What must be remembered is resource type. Here, we register a resource of type *"my_res"*. This is the type name. The 110 | engine does not really care about type name, but type identifier, the integer returned by 111 | ``zend_register_list_destructors_ex()``. You should remember it somewhere, like we do in the ``res_num`` variable. 112 | 113 | Fetching back resources 114 | *********************** 115 | 116 | Now that we registered a resource and put it in a ``zval`` for an example, we should learn how to fetch back that 117 | resource from the userland. Remember, the resource is stored into the ``zval``. Into the resource is stored the resource 118 | type number (on the ``type`` field). Thus, to be given back our resource from the user, we must extract the 119 | ``zend_resource`` from the ``zval``, and call ``zend_fetch_resource()`` to get back our ``FILE *`` pointer:: 120 | 121 | /* ... later on ... */ 122 | 123 | zval *user_zval = /* fetch zval from userland, assume type IS_RESOURCE */ 124 | 125 | ZEND_ASSERT(Z_TYPE_P(user_zval) == IS_RESOURCE); /* just a check to be sure */ 126 | 127 | fp = (FILE *)zend_fetch_resource(Z_RESVAL_P(user_zval), "my_res", res_num); 128 | 129 | Like we said : get back a zval from the user (of type ``IS_RESOURCE``), and fetch the resource pointer back from it by 130 | calling ``zend_fetch_resource()``. 131 | 132 | That function will check if the type of the resource is of the type you pass as third parameter (``res_num`` here). 133 | If yes, it extracts back the ``void *`` resource pointer you need and we are done. If not, then it throws a warning like 134 | *"supplied resource is not a valid {type name} resource"*. 135 | This could happen if for example you expect a resource of type "my_res", and you are given a zval with a resource of 136 | type "gzip", like one returned by ``gzopen()`` PHP function. 137 | 138 | Resource types are just a way for the engine to mix different kind of resources (of type "file", "gzip" or even "mysql 139 | connection") into the same resource table. Resource types have names, so that those can be used in error messages or in 140 | debug statement (like a ``var_dump($my_resource)``), and they also are represented as an integer used internaly to 141 | fetch back the resource pointer from it, and to register a destructor with the resource type. 142 | 143 | Referans sayma kaynakları 144 | ---------------------------- 145 | 146 | Like many other types, ``zend_resource`` is reference counted. We can see its ``zend_refcounted_h`` header. Here is the 147 | API to play with reference counting, if you need it (you shouldn't really need it on an average): 148 | 149 | * ``zend_list_delete(zend_resource *res)`` decrements refcount and destroys resource if drops to zero 150 | * ``zend_list_free(zend_resource *res)`` checks if refcount is zero, and destroys the resource if true. 151 | * ``zend_list_close(zend_resource *res)`` calls the resource destructor whatever the conditions 152 | 153 | Kalıcı kaynaklar 154 | -------------------- 155 | 156 | Persistent resources don't get destroyed at the end of the request. The classical use-case for that are persistent 157 | database connections. Those are connections that are recycled from request to request (with all the bullshit that will 158 | bring). 159 | 160 | Traditionnaly, you should not be using persistent resources, as one request will be different from the other. Reusing 161 | the same resource should really be thought about deeply before going this way. 162 | 163 | To register a persistent resource, use a persistent destructor instead of a classical one. This is done in the call 164 | to ``zend_register_list_destructors_ex()``, which API is like:: 165 | 166 | zend_register_list_destructors_ex(rsrc_dtor_func_t destructor, rsrc_dtor_func_t persistent_destructor, 167 | const char *type_name, int module_number); 168 | -------------------------------------------------------------------------------- /Book/php7/internal_types/zvals.rst: -------------------------------------------------------------------------------- 1 | Zvals 2 | ===== 3 | 4 | In this chapter the "zval" data structure, which is used to represent PHP values, is introduced. We explain the concepts 5 | behind zvals and how to use them in extension code. 6 | 7 | Contents: 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | zvals/basic_structure.rst 13 | .. 14 | zvals/memory_management.rst 15 | zvals/casts_and_operations.rst 16 | -------------------------------------------------------------------------------- /Book/php7/introduction.rst: -------------------------------------------------------------------------------- 1 | Giriş 2 | ============ 3 | 4 | This book is a collaborative effort between several PHP developers to better document and describe how PHP works 5 | internally. 6 | 7 | There are three primary goals of this book: 8 | 9 | * Document and describe how PHP internals work. 10 | * Document and describe how to extend the language with extensions. 11 | * Document and describe how you can interact with the community to develop PHP itself. 12 | 13 | This book is primarily targeted at developers who have experience in the C programming language. However, where-ever 14 | possible we will attempt to distill the information and summarize it so that developers who don't know C well, will 15 | still be able to understand the content. 16 | 17 | However, let us insist. You won't be able to achieve something productive, stable (crash free under any platform), 18 | performant and useful, if you don't know the C language. Here are some pretty nice online resources about the C 19 | language itself, its ecosystem and build tools, and Operating System APIs : 20 | 21 | * http://www.tenouk.com/ 22 | * https://en.wikibooks.org/wiki/C_Programming 23 | * http://c-faq.com/ 24 | * https://www.gnu.org/software/libc/ 25 | * http://www.faqs.org/docs/Linux-HOWTO/Program-Library-HOWTO.html 26 | * http://www.iecc.com/linker/linker10.html 27 | 28 | We also highly recommand you some books. You'll learn with them how to efficiently use the C language, and how to 29 | make it translate to efficient CPU instructions so that you can design strong/fast/reliable and secure programs. 30 | 31 | * The C Programming Language (Ritchie & Kernighan) 32 | * Advanced Topics in C Core Concepts in Data Structures 33 | * Learn C the Hard Way 34 | * The Art of Debugging with GDB DDD and Eclipse 35 | * The Linux Programming Interface 36 | * Advanced Linux Programming 37 | * Hackers Delight 38 | * Write Great Code (2 Volumes) 39 | 40 | .. note:: This book is Work-In-Progress and some chapters have not been written yet (in particular the ones on creating 41 | basic extensions and declaring functions), so if you're completely new to PHP extension development you'll have to 42 | wait until the remaining introductory chapters are published or start off with other 43 | `resources on the topic `_. 44 | 45 | The repository for this book is available on GitHub_. Please report issues and provide feedback on the `issue tracker`_. 46 | 47 | .. _GitHub: https://github.com/phpinternalsbook/PHP-Internals-Book 48 | .. _issue tracker: https://github.com/phpinternalsbook/PHP-Internals-Book/issues 49 | -------------------------------------------------------------------------------- /Book/php7/memory_management.rst: -------------------------------------------------------------------------------- 1 | Bellek yönetimi 2 | ================= 3 | 4 | C programmers usually have to deal with memory by hand. With dynamic memory, the programmer allocates memory when 5 | needing and frees it when finished. Failing to free dynamic memory leads to a "memory leak", which may or may not be a 6 | bad thing. In the case of PHP, as the process could live for a virtually infinite amount of time, creating a memory leak 7 | will be dramatic. In any situation, leaking memory really translates to poorly and badly designed programs that cannot 8 | be trusted. 9 | Memory leaking is easy to understand. You ask the OS to book some part of the main machine memory for you, but you never 10 | tell it to release it back for other processes usage : you are not alone on the machine, other processes need some 11 | memory, and the OS itself as well. 12 | 13 | Also, in C, memory areas are clearly bound. Reading or writing before or after the bounds is a very nasty operation. 14 | That will lead for sure to a crash, or worse an exploitable security issue. There are no magical things like 15 | auto-resizeable areas with the C language. You must clearly tell the machine (and the CPU) what you want it to do. No 16 | guess, no magic, no automation of any kind (like garbage collection). 17 | 18 | PHP's got a very specific memory model, and provides its own layer over the traditionnal libc's dynamic memory 19 | allocator. This layer is called **Zend Memory Manager**. 20 | 21 | This chapter explains you what Zend Memory Manager is, how you must use it, and what you must/must not do with it. 22 | After that, we'll quickly introduce you to specific tools used in the C area to debug dynamic memory. 23 | 24 | .. note:: If you need, please get some (possibly strong) knowledge about C memory allocation classes (static vs 25 | dynamic memory), and about libc's allocator. 26 | 27 | Contents: 28 | 29 | .. toctree:: 30 | :maxdepth: 2 31 | 32 | memory_management/zend_memory_manager.rst 33 | memory_management/memory_debugging.rst 34 | -------------------------------------------------------------------------------- /Book/php7/memory_management/memory_debugging.rst: -------------------------------------------------------------------------------- 1 | Bellekte hata ayıklama 2 | ====================== 3 | 4 | -------------------------------------------------------------------------------- /Book/php7/memory_management/zend_memory_manager.rst: -------------------------------------------------------------------------------- 1 | Zend Bellek Yöneticisi 2 | ====================== 3 | 4 | Zend Memory Manager, often abbreviated as ZendMM or ZMM, is a C layer that aims to provide abilities to allocate and 5 | release dynamic **request-bound** memory. 6 | 7 | Note the "request-bound" in the above sentence. 8 | 9 | ZendMM is not just a classical layer over libc's dynamic memory allocator, mainly represented by the couple API calls 10 | ``malloc()/free()``. ZendMM is about request-bound memory that PHP must allocate while treating a request. 11 | 12 | PHP'nin iki ana dinamik bellek havuzu 13 | ************************************* 14 | 15 | PHP is a share-nothing architecture. Well, not at 100%. Let us explain. 16 | 17 | .. note:: You may need to read :doc:`the PHP lifecycle chapter <../extensions_design/php_lifecycle>` before continuing 18 | here, you'll get additionnal informations about the different steps and cycles that can be drawn from PHP 19 | lifetime. 20 | 21 | PHP can treat several (dozen, thousands ?) of requests into the same process. By default, PHP will forget anything it 22 | knows of the current request, when that later finishes. 23 | 24 | "Forgetting" things translates to freeing any dynamic buffer that got allocated while treating a request. That means 25 | that when in the process of treating a request, one must not allocate dynamic memory using traditionnal libc calls. 26 | Doing that is perfectly valid, but you give a chance to forget to free such a buffer. 27 | 28 | ZendMM comes with an API that substitute to libc's dynamic allocator, by copying its API. When in the process of 29 | treating a request, the programmer must use that API instead of libc's allocator. 30 | 31 | For example, when PHP treats a request, it will parse PHP files. Those ones will lead to functions and classes 32 | declarations, for example. When the compiler comes to compile the PHP files, it will allocate some dynamic memory to 33 | store classes and functions it discovers. But, at the end of the request, PHP will forget about those latter. By 34 | default, PHP forgets *a very huge number* of informations from one request to another. 35 | 36 | There exists however some pretty rare informations you need to persist across several requests. But that's uncommon. 37 | 38 | What could be kept unchanged throught requests ? What we call **persistent** objects. Once more let us insist : those 39 | are rare cases. For example, the current PHP executable path won't change from requests to requests. That latter 40 | information is allocated permanently, that means it is allocated with a traditionnal libc's ``malloc()`` call. 41 | 42 | What else ? Some strings. For example, the "_SERVER" string will be reused from request to request, as every request 43 | will create the ``$_SERVER`` PHP array. So the "_SERVER" string itself can be permanently allocated, because it will be 44 | allocated once 45 | 46 | What you must remember: 47 | 48 | * There exists two kinds of dynamic memory allocations while programming PHP (or extensions): 49 | * Request-bound dynamic allocations 50 | * Permanent dynamic allocations 51 | 52 | * Request-bound dynamic memory allocations 53 | * Must only be performed when PHP is treating a request (not before, nor after) 54 | * Can only be performed using the ZendMM dynamic memory allocation API 55 | * Are very common, basically 95% of your dynamic allocations will be request-bound 56 | * Are tracked by ZendMM, and you'll be informed about bad usage of the memory area, or if you leak 57 | 58 | * Permanent dynamic memory allocations 59 | * Should not be performed while PHP is treating a request (not forbidden, but a bad idea) 60 | * Are not tracked by ZendMM, and you won't be informed about bad usage of the memory area, or if you leak 61 | * Should be pretty rare in an extension 62 | 63 | Also, keep in mind that all PHP source code has been based on such a memory level. Thus, many internal structures get 64 | allocated using the Zend Memory Manager. Most of them got a "persistent" API call, which when used, lead to 65 | traditionnal libc allocation. 66 | 67 | Here is a request-bound allocated :doc:`zend_string <../internal_types/strings/zend_strings>`:: 68 | 69 | zend_string *foo = zend_string_init("foo", strlen("foo"), 0); 70 | 71 | And here is the persistent allocated one:: 72 | 73 | zend_string *foo = zend_string_init("foo", strlen("foo"), 1); 74 | 75 | Same for :doc:`HashTable <../internal_types/hashtables>`. Request-bound allocated one:: 76 | 77 | zend_array ar; 78 | zend_hash_init(&ar, 8, NULL, NULL, 0); 79 | 80 | Persistent allocated one:: 81 | 82 | zend_array ar; 83 | zend_hash_init(&ar, 8, NULL, NULL, 1); 84 | 85 | It is always the same in all the different Zend APIs. Usually, it is weither a "0" to pass as last parameter to mean 86 | "I want this structure to be allocated using ZendMM, so request-bound", or "1" meaning "I want this structure to get 87 | allocated bypassing ZendMM and using a traditionnal libc's ``malloc()`` call". 88 | 89 | Obviously, those structures provide an API that remembers how it did allocate the structure, to use the right 90 | deallocation function when destroyed. Hence in such a code:: 91 | 92 | zend_string_release(foo); 93 | zend_hash_destroy(&ar); 94 | 95 | The API knows whether those structures were allocated using request-bound allocation, or permanent one, and in the 96 | first case will use ``efree()`` to release it, and in the second case libc's ``free()``. 97 | 98 | Zend Bellek Yöneticisi API 99 | ************************** 100 | 101 | The API is located into 102 | `Zend/zend_alloc.h `_ 103 | 104 | The API calls are mainly C macros and not functions, so get prepared if you debug them and want to look at how they 105 | work. Those calls copy libc's calls, they usually add an "e" in the function name; So you should not be lost, and there 106 | is not many things to detail about the API. 107 | 108 | Basically what you'll use most are ``emalloc(size_t)`` and ``efree(void *)``. 109 | 110 | You are also provided with ``ecalloc(size_t nmemb, size_t size)`` that allocates ``nmemb`` of individual size ``size``, 111 | and zeroes the area. If you are a strong C programmer with experience, you should know that whenever possible, it is 112 | better to use ``ecalloc()`` over ``emalloc()`` as ``ecalloc()`` will zero out the memory area which could help a lot in 113 | pointer bug detection. Remember that ``emalloc()`` works basically like the libc ``malloc()``: it will look for a big 114 | enough area in different pools, and return you the best fit. So you may be given a recycled pointer which points to 115 | garbage. 116 | 117 | Then comes ``safe_emalloc(size_t nmemb, size_t size, size_t offset)``, which is an ``emalloc(size * nmemb + offset)`` 118 | but that does check against overflows for you. You should use this API call if the numbers you must provide come from an 119 | untrusted source, like the userland. 120 | 121 | About string facilities, ``estrdup(char *)`` and ``estrndup(char *, size_t len)`` allow to duplicate strings or binary 122 | strings. 123 | 124 | Whatever happens, pointers returned by ZendMM must be freed using ZendMM, aka ``efree()`` call and 125 | **not libc's free()**. 126 | 127 | Zend Bellek Yöneticisi hata ayıklama kalkanı 128 | ******************************************** 129 | 130 | ZendMM provides the following abilities: 131 | 132 | * Memory consumption management. 133 | * Memory leak tracking. 134 | * Buffer overflows or underflows. 135 | 136 | Memory consumption management 137 | ----------------------------- 138 | 139 | ZendMM is the layer behind the PHP userland "memory_limit" feature. Every single byte allocated using the ZendMM layer 140 | is counted and added. When the INI's *memory_limit* is reached, you know what happens. 141 | That also mean that any allocation you perform via ZendMM is reflected in the ``memory_get_usage()`` call from PHP 142 | userland. 143 | 144 | As an extension developper, this is a good thing, because it helps mastering the PHP process' heap size. 145 | 146 | If a memory limit error is launched, the engine will bail out from the current code position to a catch block, and will 147 | terminate smoothly. But there is no chance it goes back to the location in your code where the limit blew up. 148 | You must be prepared to that. 149 | 150 | That means that in theory, ZendMM cannot return a NULL pointer to you. If the allocation fails from the OS, or if the 151 | allocation generates a memory limit error, the code will run into a catch block and won't return to you allocation call. 152 | 153 | If for any reason you need to bypass that protection, you must then use a traditionnal libc call, like ``malloc()``. 154 | Take care however and know what you do. It may happen that you need to allocate lots of memory and could blow up the PHP 155 | *memory_limit* if using ZendMM. Thus use another allocator (like libc) but take care: your extension will grow the 156 | current process heap size. That cannot be seen using ``memory_get_usage()`` in PHP, but by analyzing the current heap 157 | with the OS facilities (like */proc/{pid}/maps*) 158 | 159 | .. note:: If you need to fully disable ZendMM, you can launch PHP with the ``USE_ZEND_ALLOC=0`` env var. This way, every 160 | call to the ZendMM API (like ``emalloc()``) will be directed to a libc call, and ZendMM will be disabled. 161 | This is especially useful when :doc:`debugging memory <./memory_debugging>`. 162 | 163 | Memory leak tracking 164 | -------------------- 165 | 166 | Remember the main ZendMM rules: it starts when a request starts, it then expects you call its API when in need of 167 | dynamic memory as you are treating a request. When the current request ends, ZendMM shuts down. 168 | 169 | By shutting down, it will browse every of its active pointer, and if using 170 | :doc:`a debug build<../build_system/building_php>` of PHP, it will warn you about memory leaking. 171 | 172 | Let's be clear here: if at the end of the current request ZendMM finds some active memory blocks, that means those are 173 | leaking. There should not be any active memory block living onto ZendMM heap at the end of the request, as anyone who 174 | allocated some should have freed them. 175 | 176 | If you forget to free blocks, they will all get displayed on *stderr*. This process of memory leak reporting only works 177 | in the following conditions: 178 | 179 | * You are using :doc:`a debug build<../build_system/building_php>` of PHP 180 | * You have report_memleaks=On in php.ini (default) 181 | 182 | Here is an example of a simple leak into an extension:: 183 | 184 | PHP_RINIT_FUNCTION(example) 185 | { 186 | void *foo = emalloc(128); 187 | } 188 | 189 | When launching PHP with that extension activated, on a debug build, that generates on stderr:: 190 | 191 | [Fri Jun 9 16:04:59 2017] Script: '/tmp/foobar.php' 192 | /path/to/extension/file.c(123) : Freeing 0x00007fffeee65000 (128 bytes), script=/tmp/foobar.php 193 | === Total 1 memory leaks detected === 194 | 195 | Those lines are generated when the Zend Memory Manager shuts down, that is at the end of each treated request. 196 | 197 | Beware however: 198 | 199 | * Obviously ZendMM doesn't know anything about persistent allocations, or allocations that were perform in another way 200 | than using it. Hence, ZendMM can only warn you about allocations it is aware of, every traditionnal libc allocation 201 | won't be reported in here. 202 | * If PHP shuts down in an incorrect maner (what we call an unclean shutdown), ZendMM will report tons of leaks. This is 203 | because when incorrectly shutdown, the engine uses a longjmp() call to a catch block, preventing every code that cleans 204 | memory to fire-in. Thus, many leaks get reported. This happens especially after a call to PHP's exit()/die(), or if a 205 | fatal error gets triggered in some critical parts of PHP. 206 | * If you use a non-debug build of PHP, nothing shows, ZendMM is dumb. 207 | 208 | What you must remember is that ZendMM leak tracking is a nice bonus tool to have, but it does not replace a 209 | :doc:`true C memory debugger <./memory_debugging>`. 210 | 211 | 212 | Buffer overflows or underflows 213 | ------------------------------ 214 | 215 | Zend Bellek Yöneticisi motoru 216 | ***************************** 217 | 218 | ZendMM substitutes to libc's API by providing a very similar one. That API should only be used when treating requests. 219 | 220 | ZendMM encapsulates libc's allocator, and like this later, it asks for memory, arange the memory areas, sticks header 221 | and canary blocks against it, and gives you back the buffer you asked. 222 | -------------------------------------------------------------------------------- /Book/zvals.rst: -------------------------------------------------------------------------------- 1 | Zvals 2 | ===== 3 | 4 | .. 5 | Writing this I'm assuming that in the previous chapter basic extension syntax was introduced und people know how to 6 | define a function without arginfo or zpp. So this chapter can have PHP_FUNCTION examples, just without heavy zpp 7 | usage 8 | 9 | In this chapter the "zval" data structure, which is used to represent PHP values, is introduced. We explain the concepts 10 | behind zvals and how to use them in extension code. 11 | 12 | Contents: 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | zvals/basic_structure.rst 18 | zvals/memory_management.rst 19 | zvals/casts_and_operations.rst -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP-Internals-Book 2 | ================== 3 | 4 | Doküman Formatı: RST 5 | -------------------- 6 | 7 | Kitap ReStructured Text ile yazılıp Sphinx ile üretilmektedir. 8 | 9 | * RST manual: http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html 10 | * RST quickref: http://docutils.sourceforge.net/docs/user/rst/quickref.html 11 | * Sphinx manual: http://sphinx.pocoo.org/markup/index.html 12 | 13 | Kod Stili 14 | ------------ 15 | 16 | Aşağıdaki kod stilleri yazı yazarken geçerlidir, kodlamalarda değil. 17 | 18 | * Düz yazılar için maksimum satır 120 karakter olmalıdır. 19 | * Kodlar için maksimum satır 98 karakter olmalıdır. Girintilemeler olduğunda limit 102 karakter olabilir ancak PDF türetilirken hata çıkmaması için bu son limit. 20 | * Girintileme 4 karakter boşluk olmalıdır. 21 | * Satırlarda whitespace olmamalıdır. 22 | * `?`, `!` veya `:` gibi noktalamalar kelimeden hemen sonra yazılmalıdır. (Örnek: `foo:` doğru kullanım, `foo :` yanlış kullanım). 23 | 24 | Alan adları 25 | ------- 26 | 27 | Aşağıdaki alan adları Anthony'e ait olup kitabın yayınlandığı orjinal(İngilizce) adreslerdir: 28 | 29 | * phpinternalsbook.com 30 | * phpcorebook.com 31 | * insidephpbook.com 32 | 33 | Bunlardan sadece ilk olanı aktif olarak kullanımdadır. 34 | 35 | Türkçe versiyonu İstanbul PHP topluluğu tarafından çevirilmektedir. Türkçe kitaba ulaşmak için: 36 | 37 | * phpinternalsbook.istanbulphp.org 38 | 39 | 40 | Yazarlar 41 | ------- 42 | 43 | * Julien Pauli: jpauli@php.net 44 | * Nikita Popov: nikic@php.net 45 | * Anthony Ferrara: ircmaxell@php.net 46 | 47 | Çeviriler 48 | ------- 49 | 50 | * Tolga Boztuna: tboztuna@yahoo.com 51 | * Emir Karşıyakalı: emirkarsiyakali@gmail.com -------------------------------------------------------------------------------- /build_html.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -d "./BookHTML" ] && [ "$1" == "--force" ] 4 | then 5 | echo "Removing the old files." 6 | rm -rf ./BookHTML/ 7 | fi 8 | 9 | sphinx-build -b html -d BookHTML/doctrees Book BookHTML/html 10 | -------------------------------------------------------------------------------- /build_release_html.sh: -------------------------------------------------------------------------------- 1 | # get rid of old files, so we don't keep them around in the git repo 2 | # when a file or directory was renamed 3 | rm -rf BookHTML/html/*/ 4 | rm -f BookHTML/html/*.html 5 | rm -f BookHTML/html/.buildinfo 6 | 7 | sphinx-build -b html -d BookHTML/doctrees -a Book BookHTML/html -------------------------------------------------------------------------------- /build_release_latex.sh: -------------------------------------------------------------------------------- 1 | rm -rf BookLatex 2 | # inkscape Book/hashtables/images/basic_hashtable.svg -D -A Book/hashtables/images/basic_hashtable.pdf 3 | # inkscape Book/hashtables/images/doubly_linked_hashtable.svg -D -A Book/hashtables/images/doubly_linked_hashtable.pdf 4 | # inkscape Book/hashtables/images/ordered_hashtable.svg -D -A Book/hashtables/images/ordered_hashtable.pdf 5 | sphinx-build -b latex -d BookLatex/doctrees -a Book BookLatex/latex 6 | cd BookLatex/latex 7 | pdflatex PHPInternalsBook.tex 8 | pdflatex PHPInternalsBook.tex 9 | cd ../.. --------------------------------------------------------------------------------