Remove product literal strings in "pht()", part 5
[phabricator.git] / src / docs / contributor / adding_new_classes.diviner
blobea932eba5c14518f39db30118c49cad0a913fd46
1 @title Adding New Classes
2 @group developer
4 Guide to adding new classes to extend Phabricator.
6 Overview
7 ========
9 Phabricator is highly modular, and many parts of it can be extended by adding
10 new classes. This document explains how to write new classes to change or
11 expand the behavior of Phabricator.
13 IMPORTANT: The upstream does not offer support with extension development.
15 Fundamentals
16 ============
18 Phabricator primarily discovers functionality by looking at concrete subclasses
19 of some base class. For example, Phabricator determines which applications are
20 available by looking at all of the subclasses of
21 @{class@phabricator:PhabricatorApplication}. It
22 discovers available workflows in `arc` by looking at all of the subclasses of
23 @{class@arcanist:ArcanistWorkflow}. It discovers available locales
24 by looking at all of the subclasses of @{class@arcanist:PhutilLocale}.
26 This pattern holds in many cases, so you can often add functionality by adding
27 new classes with no other work. Phabricator will automatically discover and
28 integrate the new capabilities or features at runtime.
30 There are two main ways to add classes:
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.
39 The next sections walk through these approaches in greater detail.
42 Extensions Directory
43 ====================
45 The easiest way to extend Phabricator by adding new classes is to drop them
46 into the extensions directory, at `phabricator/src/extensions/`.
48 This is intended as a quick way to add small pieces of functionality, test new
49 features, or get started on a larger project. Extending Phabricator like this
50 imposes a small performance penalty compared to using a library.
52 This directory exists in all libphutil libraries, so you can find a similar
53 directory in `arcanist/src/extensions/`.
55 For example, to add a new application, create a file like this one and add it
56 to `phabricator/src/extensions/`.
58 ```name=phabricator/src/extensions/ExampleApplication.php, lang=php
59 <?php
61 final class ExampleApplication extends PhabricatorApplication {
63   public function getName() {
64     return pht('Example');
65   }
68 ```
70 If you load {nav Applications} in the web UI, you should now see your new
71 application in the list. It won't do anything yet since you haven't defined
72 any interesting behavior, but this is the basic building block of Phabricator
73 extensions.
76 Creating Libraries
77 ==================
79 A more powerful (but more complicated) way to extend Phabricator is to create
80 a libphutil library. Libraries can organize a larger amount of code, are easier
81 to work with and distribute, and have slightly better performance than loose
82 source files in the extensions directory.
84 In general, you'll perform these one-time setup steps to create a library:
86   - Create a new directory.
87   - Use `arc liberate` to initialize and name the library.
88   - Configure Phabricator or Arcanist to load the library.
90 Then, to add new code, you do this:
92   - Write or update classes.
93   - Update the library metadata by running `arc liberate` again.
95 Initializing a Library
96 ======================
98 To create a new libphutil library, create a directory for it and run
99 `arc liberate` on the directory. This documentation will use a conventional
100 directory layout, which is recommended, but you are free to deviate from this.
103 $ mkdir libcustom/
104 $ cd libcustom/
105 libcustom/ $ arc liberate src/
108 Now you'll get a prompt like this:
110 ```lang=txt
111 No library currently exists at that path...
112 The directory '/some/path/libcustom/src' does not exist.
114   Do you want to create it? [y/N] y
115 Creating new libphutil library in '/some/path/libcustom/src'.
116 Choose a name for the new library.
118   What do you want to name this library?
121 Choose a library name (in this case, "libcustom" would be appropriate) and it
122 you should get some details about the library initialization:
124 ```lang=txt
125 Writing '__phutil_library_init__.php' to
126   '/some/path/libcustom/src/__phutil_library_init__.php'...
127 Using library root at 'src'...
128 Mapping library...
129 Verifying library...
130 Finalizing library map...
131   OKAY   Library updated.
134 This will write three files:
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 libphutil 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`.
146 Linking with Phabricator
147 ========================
149 If you aren't using this library with Phabricator (e.g., you are only using it
150 with Arcanist or are building something else on libphutil) you can skip this
151 step.
153 But, if you intend to use this library with Phabricator, you need to define its
154 dependency on Phabricator by creating a `.arcconfig` file which points at
155 Phabricator. For example, you might write this file to
156 `libcustom/.arcconfig`:
158 ```lang=json
160   "load": [
161     "phabricator/src/"
162   ]
166 For details on creating a `.arcconfig`, see
167 @{article:Arcanist User Guide: Configuring a New Project}. In general, this
168 tells `arc liberate` that it should look for symbols in Phabricator when
169 performing static analysis.
171 NOTE: If Phabricator isn't located next to your custom library, specify a
172 path which actually points to the `phabricator/` directory.
174 You do not need to declare dependencies on `arcanist`, since `arc liberate`
175 automatically loads them.
177 Finally, edit your Phabricator config to tell it to load your library at
178 runtime, by adding it to `load-libraries`:
180 ```lang=json
182 'load-libraries' => array(
183   'libcustom' => 'libcustom/src/',
188 Now, Phabricator will be able to load classes from your custom library.
191 Writing Classes
192 ===============
194 To actually write classes, create a new module and put code in it:
196   libcustom/ $ mkdir src/example/
197   libcustom/ $ nano src/example/ExampleClass.php # Edit some code.
199 Now, run `arc liberate` to regenerate the static resource map:
201   libcustom/ $ arc liberate src/
203 This will automatically regenerate the static map of the library.
206 What You Can Extend And Invoke
207 ==============================
209 Arcanist and Phabricator are strict about extensibility of classes and
210 visibility of methods and properties. Most classes are marked `final`, and
211 methods have the minimum required visibility (protected or private). The goal
212 of this strictness is to make it clear what you can safely extend, access, and
213 invoke, so your code will keep working as the upstream changes.
215 IMPORTANT: We'll still break APIs frequently. The upstream does not support
216 extension development, and none of these APIs are stable.
218 When developing libraries to work with Arcanist and Phabricator, you should
219 respect method and property visibility.
221 If you want to add features but can't figure out how to do it without changing
222 Phabricator code, here are some approaches you may be able to take:
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. Note that we **do not support** extension
232     development so you may have mixed luck with this one.
234 These approaches are **discouraged**, but also possible:
236   - {icon times, color=red} **Fork**: Create an ad-hoc local fork and remove
237     `final` in your copy of the code. This will make it more difficult for you
238     to upgrade in the future, although it may be the only real way forward
239     depending on what you're trying to do.
240   - {icon times, color=red} **Use Reflection**: You can use
241     [[ http://php.net/manual/en/book.reflection.php | Reflection ]] to remove
242     modifiers at runtime. This is fragile and discouraged, but technically
243     possible.
244   - {icon times, color=red} **Remove Modifiers**: Send us a patch removing
245     `final` (or turning `protected` or `private` into `public`). We will almost
246     never accept these patches unless there's a very good reason that the
247     current behavior is wrong.
250 Next Steps
251 ==========
253 Continue by:
255   - visiting the [[ https://secure.phabricator.com/w/community_resources/ |
256     Community Resources ]] page to find or share extensions and libraries.