@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 255 lines 8.9 kB view raw
1@title Adding New Classes 2@group developer 3 4Guide to adding new classes to extend Phorge. 5 6Overview 7======== 8 9Phorge is highly modular, and many parts of it can be extended by adding 10new classes. This document explains how to write new classes to change or 11expand the behavior of Phorge. 12 13NOTE: The upstream offers limited support with extension development. 14 15Fundamentals 16============ 17 18Phorge primarily discovers functionality by looking at concrete subclasses 19of some base class. For example, Phorge determines which applications are 20available by looking at all of the subclasses of 21@{class:PhabricatorApplication}. It 22discovers available workflows in `arc` by looking at all of the subclasses of 23@{class@arcanist:ArcanistWorkflow}. It discovers available locales 24by looking at all of the subclasses of @{class@arcanist:PhutilLocale}. 25 26This pattern holds in many cases, so you can often add functionality by adding 27new classes with no other work. Phorge will automatically discover and 28integrate the new capabilities or features at runtime. 29 30There are two main ways to add classes: 31 32 - **Extensions Directory**: This is a simple way to add new code. It is 33 less powerful, but takes a lot less work. This is good for quick changes, 34 testing and development, or getting started on a larger project. 35 - **Creating Libraries**: This is a more advanced and powerful way to 36 organize extension code. This is better for larger or longer-lived 37 projects, or any code which you plan to distribute. 38 39The next sections walk through these approaches in greater detail. 40 41 42Extensions Directory 43==================== 44 45The easiest way to extend Phorge by adding new classes is to drop them 46into the extensions directory, at `phorge/src/extensions/`. 47 48This is intended as a quick way to add small pieces of functionality, test new 49features, or get started on a larger project. Extending Phorge like this 50imposes a small performance penalty compared to using a library. 51 52This directory also exists for Arcanist, so you can find a similar 53directory in `arcanist/src/extensions/`. 54 55For example, to add a new application, create a file like this one and add it 56to `phorge/src/extensions/`. 57 58```name=phorge/src/extensions/ExampleApplication.php, lang=php 59<?php 60 61final class ExampleApplication extends PhabricatorApplication { 62 63 public function getName() { 64 return pht('Example'); 65 } 66 67} 68``` 69 70If you load {nav Applications} in the web UI, you should now see your new 71application in the list. It won't do anything yet since you haven't defined 72any interesting behavior, but this is the basic building block of Phorge 73extensions. 74 75 76Creating Libraries 77================== 78 79A more powerful (but more complicated) way to extend Phorge is to create 80a library. Libraries can organize a larger amount of code, are easier 81to work with and distribute, and have slightly better performance than loose 82source files in the extensions directory. 83 84In general, you'll perform these one-time setup steps to create a library: 85 86 - Create a new directory. 87 - Use `arc liberate` to initialize and name the library. 88 - Configure Phorge or Arcanist to load the library. 89 90Then, to add new code, you do this: 91 92 - Write or update classes. 93 - Update the library metadata by running `arc liberate` again. 94 95Initializing a Library 96====================== 97 98To create a new library, create a directory for it and run 99`arc liberate` on the directory. This documentation will use a conventional 100directory layout, which is recommended, but you are free to deviate from this. 101 102``` 103$ mkdir libcustom/ 104$ cd libcustom/ 105libcustom/ $ arc liberate src/ 106``` 107 108Now you'll get a prompt like this: 109 110```lang=txt 111No library currently exists at that path... 112The directory '/some/path/libcustom/src' does not exist. 113 114 Do you want to create it? [y/N] y 115Creating new library in '/some/path/libcustom/src'. 116Choose a name for the new library. 117 118 What do you want to name this library? 119``` 120 121Choose a library name (in this case, "libcustom" would be appropriate) and it 122you should get some details about the library initialization: 123 124```lang=txt 125Writing '__phutil_library_init__.php' to 126 '/some/path/libcustom/src/__phutil_library_init__.php'... 127Using library root at 'src'... 128Mapping library... 129Verifying library... 130Finalizing library map... 131 OKAY Library updated. 132``` 133 134This will write three files: 135 136 - `src/.phutil_module_cache` This is a cache which makes "arc liberate" 137 faster when you run it to update the library. You can safely remove it at 138 any time. If you check your library into version control, you can add this 139 file to ignore rules (like `.gitignore`). 140 - `src/__phutil_library_init__.php` This records the name of the library and 141 tells Arcanist that a library exists here. 142 - `src/__phutil_library_map__.php` This is a map of all the symbols 143 (functions and classes) in the library, which allows them to be autoloaded 144 at runtime and dependencies to be statically managed by `arc liberate`. 145 146Linking with Phorge 147=================== 148 149(NOTE) If you aren't using this library with Phorge (e.g., you are only using it 150with Arcanist or are building something else) you can skip this 151step. 152 153But, if you intend to use this library with Phorge, you need to define its 154dependency on Phorge by creating a `.arcconfig` file which points at 155Phorge. For example, you might write this file to 156`libcustom/.arcconfig`: 157 158```lang=json 159{ 160 "load": [ 161 "phorge/src/" 162 ] 163} 164``` 165 166For details on creating a `.arcconfig`, see 167@{article:Arcanist User Guide: Configuring a New Project}. In general, this 168tells `arc liberate` that it should look for symbols in Phorge when 169performing static analysis. 170 171NOTE: If Phorge isn't located next to your custom library, specify a 172path which actually points to the `phorge/` directory. 173 174You do not need to declare dependencies on `arcanist`, since `arc liberate` 175automatically loads them. 176 177Finally, edit your Phorge config to tell it to load your library at 178runtime, by adding it to `load-libraries`: 179 180```lang=json 181... 182'load-libraries' => array( 183 'libcustom' => 'libcustom/src/', 184), 185... 186``` 187 188Now, Phorge will be able to load classes from your custom library. 189 190 191Writing Classes 192=============== 193 194To actually write classes, create a new module and put code in it: 195 196 libcustom/ $ mkdir src/example/ 197 libcustom/ $ nano src/example/ExampleClass.php # Edit some code. 198 199Now, run `arc liberate` to regenerate the static resource map: 200 201 libcustom/ $ arc liberate src/ 202 203This will automatically regenerate the static map of the library. 204 205 206What You Can Extend And Invoke 207============================== 208 209Arcanist and Phorge are strict about extensibility of classes and 210visibility of methods and properties. Most classes are marked `final`, and 211methods have the minimum required visibility (protected or private). The goal 212of this strictness is to make it clear what you can safely extend, access, and 213invoke, so your code will keep working as the upstream changes. 214 215IMPORTANT: We'll still break APIs frequently. The upstream offers limited 216 support for extension development, and none of these APIs are stable. 217 218When developing libraries to work with Arcanist and Phorge, you should 219respect method and property visibility. 220 221If you want to add features but can't figure out how to do it without changing 222Phorge code, here are some approaches you may be able to take: 223 224 - {icon check, color=green} **Use Composition**: If possible, use composition 225 rather than extension to build your feature. 226 - {icon check, color=green} **Find Another Approach**: Check the 227 documentation for a better way to accomplish what you're trying to do. 228 - {icon check, color=green} **File a Feature Request**: Let us know what your 229 use case is so we can make the class tree more flexible or configurable, or 230 point you at the right way to do whatever you're trying to do, or explain 231 why we don't let you do it. 232 233These approaches are **discouraged**, but also possible: 234 235 - {icon times, color=red} **Fork**: Create an ad-hoc local fork and remove 236 `final` in your copy of the code. This will make it more difficult for you 237 to upgrade in the future, although it may be the only real way forward 238 depending on what you're trying to do. 239 - {icon times, color=red} **Use Reflection**: You can use 240 [[ http://php.net/manual/en/book.reflection.php | Reflection ]] to remove 241 modifiers at runtime. This is fragile and discouraged, but technically 242 possible. 243 - {icon times, color=red} **Remove Modifiers**: Send us a patch removing 244 `final` (or turning `protected` or `private` into `public`). We will almost 245 never accept these patches unless there's a very good reason that the 246 current behavior is wrong. 247 248 249Next Steps 250========== 251 252Continue by: 253 254 - visiting the [[ https://we.phorge.it/w/community_resources/ | 255 Community Resources ]] page to find or share extensions and libraries.