Select the types of activity you want to include in your feed.
@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
···11+@title Adding New Classes
22+@group developer
33+44+Guide to adding new classes to extend Phabricator.
55+66+Overview
77+========
88+99+Phabricator is highly modular, and many parts of it can be extended by adding
1010+new classes. This document explains how to write new classes to change or
1111+expand the behavior of Phabricator.
1212+1313+IMPORTANT: The upstream does not offer support with extension development.
1414+1515+Fundamentals
1616+============
1717+1818+Phabricator primarily discovers functionality by looking at concrete subclasses
1919+of some base class. For example, Phabricator determines which applications are
2020+available by looking at all of the subclasses of
2121+@{class@phabricator:PhabricatorApplication}. It
2222+discovers available workflows in `arc` by looking at all of the subclasses of
2323+@{class@arcanist:ArcanistWorkflow}. It discovers available locales
2424+by looking at all of the subclasses of @{class@libphutil:PhutilLocale}.
2525+2626+This pattern holds in many cases, so you can often add functionality by adding
2727+new classes with no other work. Phabricator will automatically discover and
2828+integrate the new capabilities or features at runtime.
2929+3030+There are two main ways to add classes:
3131+3232+ - **Extensions Directory**: This is a simple way to add new code. It is
3333+ less powerful, but takes a lot less work. This is good for quick changes,
3434+ testing and development, or getting started on a larger project.
3535+ - **Creating Libraries**: This is a more advanced and powerful way to
3636+ organize extension code. This is better for larger or longer-lived
3737+ projects, or any code which you plan to distribute.
3838+3939+The next sections walk through these approaches in greater detail.
4040+4141+4242+Extensions Directory
4343+====================
4444+4545+The easiest way to extend Phabricator by adding new classes is to drop them
4646+into the extensions directory, at `phabricator/src/extensions/`.
4747+4848+This is intended as a quick way to add small pieces of functionality, test new
4949+features, or get started on a larger project. Extending Phabricator like this
5050+imposes a small performance penalty compared to using a library.
5151+5252+This directory exists in all libphutil libraries, so you can find similar
5353+directories in `arcanist/src/extensions/` and `libphutil/src/extensions/`.
5454+5555+For example, to add a new application, create a file like this one and add it
5656+to `phabricator/src/extensions/`.
5757+5858+```name=phabricator/src/extensions/ExampleApplication.php, lang=php
5959+<?php
6060+6161+final class ExampleApplication extends PhabricatorApplication {
6262+6363+ public function getName() {
6464+ return pht('Example');
6565+ }
6666+6767+}
6868+```
6969+7070+If you load {nav Applications} in the web UI, you should now see your new
7171+application in the list. It won't do anything yet since you haven't defined
7272+any interesting behavior, but this is the basic building block of Phabricator
7373+extensions.
7474+7575+7676+Creating Libraries
7777+==================
7878+7979+A more powerful (but more complicated) way to extend Phabricator is to create
8080+a libphutil library. Libraries can organize a larger amount of code, are easier
8181+to work with and distribute, and have slightly better performance than loose
8282+source files in the extensions directory.
8383+8484+In general, you'll perform these one-time setup steps to create a library:
8585+8686+ - Create a new directory.
8787+ - Use `arc liberate` to initialize and name the library.
8888+ - Configure Phabricator or Arcanist to load the library.
8989+9090+Then, to add new code, you do this:
9191+9292+ - Write or update classes.
9393+ - Update the library metadata by running `arc liberate` again.
9494+9595+Initializing a Library
9696+======================
9797+9898+To create a new libphutil library, create a directory for it and run
9999+`arc liberate` on the directory. This documentation will use a conventional
100100+directory layout, which is recommended, but you are free to deviate from this.
101101+102102+```
103103+$ mkdir libcustom/
104104+$ cd libcustom/
105105+libcustom/ $ arc liberate src/
106106+```
107107+108108+Now you'll get a prompt like this:
109109+110110+```lang=txt
111111+No library currently exists at that path...
112112+The directory '/some/path/libcustom/src' does not exist.
113113+114114+ Do you want to create it? [y/N] y
115115+Creating new libphutil library in '/some/path/libcustom/src'.
116116+Choose a name for the new library.
117117+118118+ What do you want to name this library?
119119+```
120120+121121+Choose a library name (in this case, "libcustom" would be appropriate) and it
122122+you should get some details about the library initialization:
123123+124124+```lang=txt
125125+Writing '__phutil_library_init__.php' to
126126+ '/some/path/libcustom/src/__phutil_library_init__.php'...
127127+Using library root at 'src'...
128128+Mapping library...
129129+Verifying library...
130130+Finalizing library map...
131131+ OKAY Library updated.
132132+```
133133+134134+This will write three files:
135135+136136+ - `src/.phutil_module_cache` This is a cache which makes "arc liberate"
137137+ faster when you run it to update the library. You can safely remove it at
138138+ any time. If you check your library into version control, you can add this
139139+ file to ignore rules (like `.gitignore`).
140140+ - `src/__phutil_library_init__.php` This records the name of the library and
141141+ tells libphutil that a library exists here.
142142+ - `src/__phutil_library_map__.php` This is a map of all the symbols
143143+ (functions and classes) in the library, which allows them to be autoloaded
144144+ at runtime and dependencies to be statically managed by `arc liberate`.
145145+146146+Linking with Phabricator
147147+========================
148148+149149+If you aren't using this library with Phabricator (e.g., you are only using it
150150+with Arcanist or are building something else on libphutil) you can skip this
151151+step.
152152+153153+But, if you intend to use this library with Phabricator, you need to define its
154154+dependency on Phabricator by creating a `.arcconfig` file which points at
155155+Phabricator. For example, you might write this file to
156156+`libcustom/.arcconfig`:
157157+158158+```lang=json
159159+{
160160+ "load": [
161161+ "phabricator/src/"
162162+ ]
163163+}
164164+```
165165+166166+For details on creating a `.arcconfig`, see
167167+@{article:Arcanist User Guide: Configuring a New Project}. In general, this
168168+tells `arc liberate` that it should look for symbols in Phabricator when
169169+performing static analysis.
170170+171171+NOTE: If Phabricator isn't located next to your custom library, specify a
172172+path which actually points to the `phabricator/` directory.
173173+174174+You do not need to declare dependencies on `arcanist` or `libphutil`,
175175+since `arc liberate` automatically loads them.
176176+177177+Finally, edit your Phabricator config to tell it to load your library at
178178+runtime, by adding it to `load-libraries`:
179179+180180+```lang=json
181181+...
182182+'load-libraries' => array(
183183+ 'libcustom' => 'libcustom/src/',
184184+),
185185+...
186186+```
187187+188188+Now, Phabricator will be able to load classes from your custom library.
189189+190190+191191+Writing Classes
192192+===============
193193+194194+To actually write classes, create a new module and put code in it:
195195+196196+ libcustom/ $ mkdir src/example/
197197+ libcustom/ $ nano src/example/ExampleClass.php # Edit some code.
198198+199199+Now, run `arc liberate` to regenerate the static resource map:
200200+201201+ libcustom/ $ arc liberate src/
202202+203203+This will automatically regenerate the static map of the library.
204204+205205+206206+What You Can Extend And Invoke
207207+==============================
208208+209209+libphutil, Arcanist and Phabricator are strict about extensibility of classes
210210+and visibility of methods and properties. Most classes are marked `final`, and
211211+methods have the minimum required visibility (protected or private). The goal
212212+of this strictness is to make it clear what you can safely extend, access, and
213213+invoke, so your code will keep working as the upstream changes.
214214+215215+IMPORTANT: We'll still break APIs frequently. The upstream does not support
216216+extension development, and none of these APIs are stable.
217217+218218+When developing libraries to work with libphutil, Arcanist and Phabricator, you
219219+should respect method and property visibility.
220220+221221+If you want to add features but can't figure out how to do it without changing
222222+Phabricator code, here are some approaches you may be able to take:
223223+224224+ - {icon check, color=green} **Use Composition**: If possible, use composition
225225+ rather than extension to build your feature.
226226+ - {icon check, color=green} **Find Another Approach**: Check the
227227+ documentation for a better way to accomplish what you're trying to do.
228228+ - {icon check, color=green} **File a Feature Request**: Let us know what your
229229+ use case is so we can make the class tree more flexible or configurable, or
230230+ point you at the right way to do whatever you're trying to do, or explain
231231+ why we don't let you do it. Note that we **do not support** extension
232232+ development so you may have mixed luck with this one.
233233+234234+These approaches are **discouraged**, but also possible:
235235+236236+ - {icon times, color=red} **Fork**: Create an ad-hoc local fork and remove
237237+ `final` in your copy of the code. This will make it more difficult for you
238238+ to upgrade in the future, although it may be the only real way forward
239239+ depending on what you're trying to do.
240240+ - {icon times, color=red} **Use Reflection**: You can use
241241+ [[ http://php.net/manual/en/book.reflection.php | Reflection ]] to remove
242242+ modifiers at runtime. This is fragile and discouraged, but technically
243243+ possible.
244244+ - {icon times, color=red} **Remove Modifiers**: Send us a patch removing
245245+ `final` (or turning `protected` or `private` into `public`). We will almost
246246+ never accept these patches unless there's a very good reason that the
247247+ current behavior is wrong.
248248+249249+250250+Next Steps
251251+==========
252252+253253+Continue by:
254254+255255+ - visiting the [[ https://secure.phabricator.com/w/community_resources/ |
256256+ Community Resources ]] page to find or share extensions and libraries.
-60
src/docs/contributor/darkconsole.diviner
···11-@title Using DarkConsole
22-@group developer
33-44-Enabling and using the built-in debugging console.
55-66-= Overview =
77-88-DarkConsole is a debugging console built into Phabricator which exposes
99-configuration, performance and error information. It can help you detect,
1010-understand and resolve bugs and performance problems in Phabricator
1111-applications.
1212-1313-DarkConsole was originally implemented as part of the Facebook Lite site; its
1414-name is a bit of play on that (and a reference to the dark color palette its
1515-design uses).
1616-1717-= Warning =
1818-1919-Because DarkConsole exposes some configuration and debugging information, it is
2020-disabled by default (and **you should not enable it in production**). It has
2121-some simple safeguards to prevent leaking credential information, but enabling
2222-it in production may compromise the integrity of an install.
2323-2424-= Enabling DarkConsole =
2525-2626-You enable DarkConsole in your configuration, by setting `darkconsole.enabled`
2727-to `true`, and then turning it on in `Settings` -> `Developer Settings`. Once
2828-DarkConsole is enabled, you can show or hide it by pressing ##`## on your
2929-keyboard.
3030-3131-Since the setting is not available to logged-out users, you can also set
3232-`darkconsole.always-on` if you need to access DarkConsole on logged-out pages.
3333-3434-DarkConsole has a number of tabs, each of which is powered by a "plugin". You
3535-can use them to access different debugging and performance features.
3636-3737-= Plugin: Error Log =
3838-3939-The "Error Log" plugin shows errors that occurred while generating the page,
4040-similar to the httpd `error.log`. You can send information to the error log
4141-explicitly with the @{function@libphutil:phlog} function.
4242-4343-If errors occurred, a red dot will appear on the plugin tab.
4444-4545-= Plugin: Request =
4646-4747-The "Request" plugin shows information about the HTTP request the server
4848-received, and the server itself.
4949-5050-= Plugin: Services =
5151-5252-The "Services" plugin lists calls a page made to external services, like
5353-MySQL and the command line.
5454-5555-= Plugin: XHProf =
5656-5757-The "XHProf" plugin gives you access to the XHProf profiler. To use it, you need
5858-to install the corresponding PHP plugin -- see instructions in the
5959-@{article:Installation Guide}. Once it is installed, you can use XHProf to
6060-profile the runtime performance of a page.
-54
src/docs/contributor/installing_xhprof.diviner
···11-@title Installing XHProf
22-@group developer
33-44-Describes how to install XHProf, a PHP profiling tool.
55-66-Overview
77-========
88-99-You can install XHProf to activate the XHProf tab in DarkConsole and the
1010-`--xprofile` flag from the CLI. This will allow you to generate performance
1111-profiles of pages and scripts, which can be tremendously valuable in identifying
1212-and fixing slow code.
1313-1414-Installing XHProf
1515-=================
1616-1717-XHProf is a PHP profiling tool. You don't need to install it unless you are
1818-developing Phabricator and making performance changes.
1919-2020-You can install xhprof with:
2121-2222- $ pecl install xhprof
2323-2424-If you have a PEAR version prior to 1.9.3, you may run into a `phpize` failure.
2525-If so, you can download the source and build it with:
2626-2727- $ cd extension/
2828- $ phpize
2929- $ ./configure
3030- $ make
3131- $ sudo make install
3232-3333-You may also need to add `extension=xhprof.so` to your php.ini.
3434-3535-See <https://bugs.php.net/bug.php?id=59747> for more information.
3636-3737-Using XHProf: Web
3838-=================
3939-4040-To profile a web page, activate DarkConsole and navigate to the XHProf tab.
4141-Use the **Profile Page** button to generate a profile.
4242-4343-Using XHProf: CLI
4444-=================
4545-4646-From the command line, use the `--xprofile <filename>` flag to generate a
4747-profile of any script.
4848-4949-Next Steps
5050-==========
5151-5252-Continue by:
5353-5454- - enabling DarkConsole with @{article:Using DarkConsole}.
+344-31
src/docs/contributor/internationalization.diviner
···99Phabricator partially supports internationalization, but many of the tools
1010are missing or in a prototype state.
11111212-This document very briefly summarizes some of what exists today.
1212+This document describes what tools exist today, how to add new translations,
1313+and how to use the translation tools to make a codebase translatable.
1414+1515+1616+Adding a New Locale
1717+===================
1818+1919+To add a new locale, subclass @{class:PhutilLocale}. This allows you to
2020+introduce a new locale, like "German" or "Klingon".
2121+2222+Once you've created a locale, applications can add translations for that
2323+locale.
2424+2525+For instructions on adding new classes, see @{article:Adding New Classes}.
2626+2727+2828+Adding Translations to Locale
2929+=============================
3030+3131+To translate strings, subclass @{class:PhutilTranslation}. Translations need
3232+to belong to a locale: the locale defines an available language, and each
3333+translation subclass provides strings for it.
3434+3535+Translations are separated from locales so that third-party applications can
3636+provide translations into different locales without needing to define those
3737+locales themselves.
3838+3939+For instructions on adding new classes, see @{article:Adding New Classes}.
4040+13411442Writing Translatable Code
1515-========
4343+=========================
16441745Strings are marked for translation with @{function@libphutil:pht}.
18461919-Adding a New Locale
2020-=========
4747+The `pht()` function takes a string (and possibly some parameters) and returns
4848+the translated version of that string in the current viewer's locale, if a
4949+translation is available.
5050+5151+If text strings will ultimately be read by humans, they should essentially
5252+always be wrapped in `pht()`. For example:
5353+5454+```lang=php
5555+$dialog->appendParagraph(pht('This is an example.'));
5656+```
5757+5858+This allows the code to return the correct Spanish or German or Russian
5959+version of the text, if the viewer is using Phabricator in one of those
6060+languages and a translation is available.
6161+6262+Using `pht()` properly so that strings are translatable can be tricky. Briefly,
6363+the major rules are:
6464+6565+ - Only pass static strings as the first parameter to `pht()`.
6666+ - Use parameters to create strings containing user names, object names, etc.
6767+ - Translate full sentences, not sentence fragments.
6868+ - Let the translation framework handle plural rules.
6969+ - Use @{class@libphutil:PhutilNumber} for numbers.
7070+ - Let the translation framework handle subject gender rules.
7171+ - Translate all human-readable text, even exceptions and error messages.
7272+7373+See the next few sections for details on these rules.
7474+7575+7676+Use Static Strings
7777+==================
7878+7979+The first parameter to `pht()` must always be a static string. Broadly, this
8080+means it should not contain variables or function or method calls (it's OK to
8181+split it across multiple lines and concatenate the parts together).
8282+8383+These are good:
8484+8585+```lang=php
8686+pht('The night is dark.');
8787+pht(
8888+ 'Two roads diverged in a yellow wood, '.
8989+ 'and sorry I could not travel both '.
9090+ 'and be one traveler, long I stood.');
9191+9292+```
9393+9494+These won't work (they might appear to work, but are wrong):
9595+9696+```lang=php, counterexample
9797+pht(some_function());
9898+pht('The duck says, '.$quack);
9999+pht($string);
100100+```
101101+102102+The first argument must be a static string so it can be extracted by static
103103+analysis tools and dumped in a big file for translators. If it contains
104104+functions or variables, it can't be extracted, so translators won't be able to
105105+translate it.
106106+107107+Lint will warn you about problems with use of static strings in calls to
108108+`pht()`.
109109+110110+111111+Parameters
112112+==========
113113+114114+You can provide parameters to a translation string by using `sprintf()`-style
115115+patterns in the input string. For example:
116116+117117+```lang=php
118118+pht('%s earned an award.', $actor);
119119+pht('%s closed %s.', $actor, $task);
120120+```
121121+122122+This is primarily appropriate for usernames, object names, counts, and
123123+untranslatable strings like URIs or instructions to run commands from the CLI.
124124+125125+Parameters normally should not be used to combine two pieces of translated
126126+text: see the next section for guidance.
127127+128128+Sentence Fragments
129129+==================
130130+131131+You should almost always pass the largest block of text to `pht()` that you
132132+can. Particularly, it's important to pass complete sentences, not try to build
133133+a translation by stringing together sentence fragments.
134134+135135+There are several reasons for this:
136136+137137+ - It gives translators more context, so they can be more confident they are
138138+ producing a satisfying, natural-sounding translation which will make sense
139139+ and sound good to native speakers.
140140+ - In some languages, one fragment may need to translate differently depending
141141+ on what the other fragment says.
142142+ - In some languages, the most natural-sounding translation may change the
143143+ order of words in the sentence.
144144+145145+For example, suppose we want to translate these sentence to give the user some
146146+instructions about how to use an interface:
147147+148148+> Turn the switch to the right.
149149+150150+> Turn the switch to the left.
151151+152152+> Turn the dial to the right.
153153+154154+> Turn the dial to the left.
155155+156156+Maybe we have a function like this:
157157+158158+```
159159+function get_string($is_switch, $is_right) {
160160+ // ...
161161+}
162162+```
163163+164164+One way to write the function body would be like this:
165165+166166+```lang=php, counterexample
167167+$what = $is_switch ? pht('switch') : pht('dial');
168168+$dir = $is_right ? pht('right') : pht('left');
169169+170170+return pht('Turn the ').$what.pht(' to the ').$dir.pht('.');
171171+```
172172+173173+This will work fine in English, but won't work well in other languages.
174174+175175+One problem with doing this is handling gendered nouns. Languages like Spanish
176176+have gendered nouns, where some nouns are "masculine" and others are
177177+"feminine". The gender of a noun affects which article (in English, the word
178178+"the" is an article) should be used with it.
179179+180180+In English, we say "**the** knob" and "**the** switch", but a Spanish speaker
181181+would say "**la** perilla" and "**el** interruptor", because the noun for
182182+"knob" in Spanish is feminine (so it is used with the article "la") while the
183183+noun for "switch" is masculine (so it is used with the article "el").
184184+185185+A Spanish speaker can not translate the string "Turn the" correctly without
186186+knowing which gender the noun has. Spanish has //two// translations for this
187187+string ("Gira el", "Gira la"), and the form depends on which noun is being
188188+used.
189189+190190+Another problem is that this reduces flexibility. Translating fragments like
191191+this locks translators into a specific word order, when rearranging the words
192192+might make the sentence sound much more natural to a native speaker.
193193+194194+For example, if the string read "The knob, to the right, turn it.", it
195195+would technically be English and most English readers would understand the
196196+meaning, but no native English speaker would speak or write like this.
197197+198198+However, some languages have different subject-verb order rules or
199199+colloquisalisms, and a word order which transliterates like this may sound more
200200+natural to a native speaker. By translating fragments instead of complete
201201+sentences, you lock translators into English word order.
202202+203203+Finally, the last fragment is just a period. If a translator is presented with
204204+this string in an interface without much context, they have no hope of guessing
205205+how it is used in the software (it could be an end-of-sentence marker, or a
206206+decimal point, or a date separator, or a currency separator, all of which have
207207+very different translations in many locales). It will also conflict with all
208208+other translations of the same string in the codebase, so even if they are
209209+given context they can't translate it without technical problems.
212102222-To add a new locale, subclass @{class:PhutilLocale}.
211211+To avoid these issues, provide complete sentences for translation. This almost
212212+always takes the form of writing out alternatives in full. This is a good way
213213+to implement the example function:
214214+215215+```lang=php
216216+if ($is_switch) {
217217+ if ($is_right) {
218218+ return pht('Turn the switch to the right.');
219219+ } else {
220220+ return pht('Turn the switch to the left.');
221221+ }
222222+} else {
223223+ if ($is_right) {
224224+ return pht('Turn the dial to the right.');
225225+ } else {
226226+ return pht('Turn the dial to the left.');
227227+ }
228228+}
229229+```
232302424-Translating Strings
2525-========
231231+Although this is more verbose, translators can now get genders correct,
232232+rearrange word order, and have far more context when translating. This enables
233233+better, natural-sounding translations which are more satisfying to native
234234+speakers.
262352727-To translate strings, subclass @{class:PhutilTranslation}.
2823629237Singular and Plural
3030-========
238238+===================
239239+240240+Different languages have various rules for plural nouns.
241241+242242+In English there are usually two plural noun forms: for one thing, and any
243243+other number of things. For example, we say that one chair is a "chair" and any
244244+other number of chairs are "chairs": "0 chairs", "1 chair", "2 chairs", etc.
312453232-Different languages have various rules for using singular and plural. All you
3333-need to do is to call @{function@libphutil:pht} with a text that is suitable for
3434-both forms. Example:
246246+In other languages, there are different (and, in some cases, more) plural
247247+forms. For example, in Czech, there are separate forms for "one", "several",
248248+and "many".
249249+250250+Because plural noun rules depend on the language, you should not write code
251251+which hard-codes English rules. For example, this won't translate well:
252252+253253+```lang=php, counterexample
254254+if ($count == 1) {
255255+ return pht('This will take an hour.');
256256+} else {
257257+ return pht('This will take hours.');
258258+}
259259+```
260260+261261+This code is hard-coding the English rule for plural nouns. In languages like
262262+Czech, the correct word for "hours" may be different if the count is 2 or 15,
263263+but a translator won't be able to provide the correct translation if the string
264264+is written like this.
265265+266266+Instead, pass a generic string to the translation engine which //includes// the
267267+number of objects, and let it handle plural nouns. This is the correct way to
268268+write the translation:
269269+270270+```lang=php
271271+return pht('This will take %s hour(s).', new PhutilNumber($count));
272272+```
273273+274274+If you now load the web UI, you'll see "hour(s)" literally in the UI. To fix
275275+this so the translation sounds better in English, provide translations for this
276276+string in the @{class@phabricator:PhabricatorUSEnglishTranslation} file:
277277+278278+```lang=php
279279+'This will take %s hour(s).' => array(
280280+ 'This will take an hour.',
281281+ 'This will take hours.',
282282+),
283283+```
284284+285285+The string will then sound natural in English, but non-English translators will
286286+also be able to produce a natural translation.
287287+288288+Note that the translations don't actually include the number in this case. The
289289+number is being passed from the code, but that just lets the translation engine
290290+get the rules right: the number does not need to appear in the final
291291+translations shown to the user.
292292+293293+Using PhutilNumber
294294+==================
352953636- pht('%d beer(s)', $count);
296296+When translating numbers, you should almost always use `%s` and wrap the count
297297+or number in `new PhutilNumber($count)`. For example:
372983838-Translators will translate this text for all different forms the language uses:
299299+```lang=php
300300+pht('You have %s experience point(s).', new PhutilNumber($xp));
301301+```
393024040- // English translation
4141- array('%d beer', '%d beers');
303303+This will let the translation engine handle plural noun rules correctly, and
304304+also format large numbers correctly in a locale-aware way with proper unit and
305305+decimal separators (for example, `1000000` may be printed as "1,000,000",
306306+with commas for readability).
423074343- // Czech translation
4444- array('%d pivo', '%d piva', '%d piv');
308308+The exception to this rule is IDs which should not be written with unit
309309+separators. For example, this is correct for an object ID:
453104646-The ugly identifier passed to @{function@libphutil:pht} will remain in the text
4747-only if the translation doesn't exist.
311311+```lang=php
312312+pht('This diff has ID %d.', $diff->getID());
313313+```
4831449315Male and Female
5050-========
316316+===============
317317+318318+Different languages also use different words for talking about subjects who are
319319+male, female or have an unknown gender. In English this is mostly just
320320+pronouns (like "he" and "she") but there are more complex rules in other
321321+languages, and languages like Czech also require verb agreement.
322322+323323+When a parameter refers to a gendered person, pass an object which implements
324324+@{interface@libphutil:PhutilPerson} to `pht()` so translators can provide
325325+gendered translation variants.
326326+327327+```lang=php
328328+pht('%s wrote', $actor);
329329+```
330330+331331+Translators will create these translations:
332332+333333+```lang=php
334334+// English translation
335335+'%s wrote';
336336+337337+// Czech translation
338338+array('%s napsal', '%s napsala');
339339+```
513405252-Different languages use different words for talking about males, females and
5353-unknown genders. Callsites have to call @{function@libphutil:pht} passing
5454-@{class:PhabricatorUser} (or other implementation of
5555-@{interface@libphutil:PhutilPerson}) if talking about the user. Example:
341341+(You usually don't need to worry very much about this rule, it is difficult to
342342+get wrong in standard code.)
563435757- pht('%s wrote', $actor);
583445959-Translators will create this translations:
345345+Exceptions and Errors
346346+=====================
603476161- // English translation
6262- '%s wrote';
348348+You should translate all human-readable text, even exceptions and error
349349+messages. This is primarily a rule of convenience which is straightforward
350350+and easy to follow, not a technical rule.
633516464- // Czech translation
6565- array('%s napsal', '%s napsala');
352352+Some exceptions and error messages don't //technically// need to be translated,
353353+as they will never be shown to a user, but many exceptions and error messages
354354+are (or will become) user-facing on some way. When writing a message, there is
355355+often no clear and objective way to determine which type of message you are
356356+writing. Rather than try to distinguish which are which, we simply translate
357357+all human-readable text. This rule is unambiguous and easy to follow.
358358+359359+In cases where similar error or exception text is often repeated, it is
360360+probably appropriate to define an exception for that category of error rather
361361+than write the text out repeatedly, anyway. Two examples are
362362+@{class@libphutil:PhutilInvalidStateException} and
363363+@{class@libphutil:PhutilMethodNotImplementedException}, which mostly exist to
364364+produce a consistent message about a common error state in a convenient way.
365365+366366+There are a handful of error strings in the codebase which may be used before
367367+the translation framework is loaded, or may be used during handling other
368368+errors, possibly rasised from within the translation framework. This handful
369369+of special cases are left untranslated to prevent fatals and cycles in the
370370+error handler.
371371+372372+373373+Next Steps
374374+==========
375375+376376+Continue by:
377377+378378+ - adding a new locale or translation file with @{article:Adding New Classes}.
+1-1
src/docs/user/configuration/custom_fields.diviner
···207207Continue by:
208208209209 - learning more about extending Phabricator with custom code in
210210- @{article:libphutil Libraries User Guide};
210210+ @{article@contributor:Adding New Classes};
211211 - or returning to the @{article: Configuration Guide}.
···109109 just those started with `phd start`. If you're writing a restart script,
110110 have it launch any custom daemons explicitly after `phd restart`.
111111 - You can write your own daemons and manage them with `phd` by extending
112112- @{class:PhabricatorDaemon}. See @{article:libphutil Libraries User Guide}.
112112+ @{class:PhabricatorDaemon}. See {article@contributor:Adding New Classes}.
113113 - See @{article:Diffusion User Guide} for details about tuning the repository
114114 daemon.
115115···137137138138 - learning about the repository daemon with @{article:Diffusion User Guide};
139139 or
140140- - writing your own daemons with @{article:libphutil Libraries User Guide}.
140140+ - writing your own daemons with {article@contributor:Adding New Classes}.
+162
src/docs/user/field/darkconsole.diviner
···11+@title Using DarkConsole
22+@group fieldmanual
33+44+Enabling and using the built-in debugging and performance console.
55+66+Overview
77+========
88+99+DarkConsole is a debugging console built into Phabricator which exposes
1010+configuration, performance and error information. It can help you detect,
1111+understand and resolve bugs and performance problems in Phabricator
1212+applications.
1313+1414+1515+Security Warning
1616+================
1717+1818+WARNING: Because DarkConsole exposes some configuration and debugging
1919+information, it is disabled by default and you should be cautious about
2020+enabling it in production.
2121+2222+Particularly, DarkConsole may expose some information about your session
2323+details or other private material. It has some crude safeguards against this,
2424+but does not completely sanitize output.
2525+2626+This is mostly a risk if you take screenshots or copy/paste output and share
2727+it with others.
2828+2929+3030+Enabling DarkConsole
3131+====================
3232+3333+You enable DarkConsole in your configuration, by setting `darkconsole.enabled`
3434+to `true`, and then turning it on in {nav Settings > Developer Settings}.
3535+3636+Once DarkConsole is enabled, you can show or hide it by pressing ##`## on your
3737+keyboard.
3838+3939+Since the setting is not available to logged-out users, you can also set
4040+`darkconsole.always-on` if you need to access DarkConsole on logged-out pages.
4141+4242+DarkConsole has a number of tabs, each of which is powered by a "plugin". You
4343+can use them to access different debugging and performance features.
4444+4545+4646+Plugin: Error Log
4747+=================
4848+4949+The "Error Log" plugin shows errors that occurred while generating the page,
5050+similar to the httpd `error.log`. You can send information to the error log
5151+explicitly with the @{function@libphutil:phlog} function.
5252+5353+If errors occurred, a red dot will appear on the plugin tab.
5454+5555+5656+Plugin: Request
5757+===============
5858+5959+The "Request" plugin shows information about the HTTP request the server
6060+received, and the server itself.
6161+6262+6363+Plugin: Services
6464+================
6565+6666+The "Services" plugin lists calls a page made to external services, like
6767+MySQL and subprocesses.
6868+6969+The Services tab can help you understand and debug issues related to page
7070+behavior: for example, you can use it to see exactly what queries or commands a
7171+page is running. In some cases, you can re-run those queries or commands
7272+yourself to examine their output and look for problems.
7373+7474+This tab can also be particularly useful in understanding page performance,
7575+because many performance problems are caused by inefficient queries (queries
7676+with bad query plans or which take too long) or repeated queries (queries which
7777+could be better structured or benefit from caching).
7878+7979+When analyzing performance problems, the major things to look for are:
8080+8181+**Summary**: In the summary table at the top of the tab, are any categories
8282+of events dominating the performance cost? For normal pages, the costs should
8383+be roughly along these lines:
8484+8585+| Event Type | Approximate Cost |
8686+|---|---|
8787+| Connect | 1%-10% |
8888+| Query | 10%-40% |
8989+| Cache | 1% |
9090+| Event | 1% |
9191+| Conduit | 0%-80% |
9292+| Exec | 0%-80% |
9393+| All Services | 10%-75% |
9494+| Entire Page | 100ms - 1000ms |
9595+9696+These ranges are rough, but should usually be what you expect from a page
9797+summary. If any of these numbers are way off (for example, "Event" is taking
9898+50% of runtime), that points toward a possible problem in that section of the
9999+code, and can guide you to examining the related service calls more carefully.
100100+101101+**Duration**: In the Duration column, look for service calls that take a long
102102+time. Sometimes these calls are just what the page is doing, but sometimes they
103103+may indicate a problem.
104104+105105+Some questions that may help understanding this column are: are there a small
106106+number of calls which account for a majority of the total page generation time?
107107+Do these calls seem fundamental to the behavior of the page, or is it not clear
108108+why they need to be made? Do some of them seem like they could be cached?
109109+110110+If there are queries which look slow, using the "Analyze Query Plans" button
111111+may help reveal poor query plans.
112112+113113+Generally, this column can help pinpoint these kinds of problems:
114114+115115+ - Queries or other service calls which are huge and inefficient.
116116+ - Work the page is doing which it could cache instead.
117117+ - Problems with network services.
118118+ - Missing keys or poor query plans.
119119+120120+**Repeated Calls**: In the "Details" column, look for service calls that are
121121+being made over and over again. Sometimes this is normal, but usually it
122122+indicates a call that can be batched or cached.
123123+124124+Some things to look for are: are similar calls being made over and over again?
125125+Do calls mostly make sense given what the page is doing? Could any calls be
126126+cached? Could multiple small calls be collected into one larger call? Are any
127127+of the service calls clearly goofy nonsense that shouldn't be happening?
128128+129129+Generally, this column can help pinpoint these kinds of problems:
130130+131131+ - Unbatched queries which should be batched (see
132132+ @{article:Performance: N+1 Query Problem}).
133133+ - Opportunities to improve performance with caching.
134134+ - General goofiness in how service calls are woking.
135135+136136+If the services tab looks fine, and particularly if a page is slow but the
137137+"All Services" cost is small, that may indicate a problem in PHP. The best
138138+tool to understand problems in PHP is XHProf.
139139+140140+141141+Plugin: XHProf
142142+==============
143143+144144+The "XHProf" plugin gives you access to the XHProf profiler. To use it, you need
145145+to install the corresponding PHP plugin.
146146+147147+Once it is installed, you can use XHProf to profile the runtime performance of
148148+a page. This will show you a detailed breakdown of where PHP spent time. This
149149+can help find slow or inefficient application code, and is the most powerful
150150+general-purpose performance tool available.
151151+152152+For instructions on installing and using XHProf, see @{article:Using XHProf}.
153153+154154+155155+Next Steps
156156+==========
157157+158158+Continue by:
159159+160160+ - installing XHProf with @{article:Using XHProf}; or
161161+ - understanding and reporting performance issues with
162162+ @{article:Troubleshooting Performance Problems}.
+179
src/docs/user/field/performance.diviner
···11+@title Troubleshooting Performance Problems
22+@group fieldmanual
33+44+Guide to the troubleshooting slow pages and hangs.
55+66+Overview
77+========
88+99+This document describes how to isolate, examine, understand and resolve or
1010+report performance issues like slow pages and hangs.
1111+1212+This document covers the general process for handling performance problems,
1313+and outlines the major tools available for understanding them:
1414+1515+ - **Multimeter** helps you understand sources of load and broad resource
1616+ utilization. This is a coarse, high-level tool.
1717+ - **DarkConsole** helps you dig into a specific slow page and understand
1818+ service calls. This is a general, mid-level tool.
1919+ - **XHProf** gives you detailed application performance profiles. This
2020+ is a fine-grained, low-level tool.
2121+2222+Performance and the Upstream
2323+============================
2424+2525+Performance issues and hangs will often require upstream involvement to fully
2626+resolve. The intent is for Phabricator to perform well in all reasonable cases,
2727+not require tuning for different workloads (as long as those workloads are
2828+generally reasonable). Poor performance with a reasonable workload is likely a
2929+bug, not a configuration problem.
3030+3131+However, some pages are slow because Phabricator legitimately needs to do a lot
3232+of work to generate them. For example, if you write a 100MB wiki document,
3333+Phabricator will need substantial time to process it, it will take a long time
3434+to download over the network, and your browser will proably not be able to
3535+render it especially quickly.
3636+3737+We may be able to improve perfomance in some cases, but Phabricator is not
3838+magic and can not wish away real complexity. The best solution to these problems
3939+is usually to find another way to solve your problem: for example, maybe the
4040+100MB document can be split into several smaller documents.
4141+4242+Here are some examples of performance problems under reasonable workloads that
4343+the upstream can help resolve:
4444+4545+ - {icon check, color=green} Commenting on a file and mentioning that same
4646+ file results in a hang.
4747+ - {icon check, color=green} Creating a new user takes many seconds.
4848+ - {icon check, color=green} Loading Feed hangs on 32-bit systems.
4949+5050+The upstream will be less able to help resolve unusual workloads with high
5151+inherent complexity, like these:
5252+5353+ - {icon times, color=red} A 100MB wiki page takes a long time to render.
5454+ - {icon times, color=red} A turing-complete simulation of Conway's Game of
5555+ Life implented in 958,000 Herald rules executes slowly.
5656+ - {icon times, color=red} Uploading an 8GB file takes several minutes.
5757+5858+Generally, the path forward will be:
5959+6060+ - Follow the instructions in this document to gain the best understanding of
6161+ the issue (and of how to reproduce it) that you can.
6262+ - In particular, is it being caused by an unusual workload (like a 100MB
6363+ wiki page)? If so, consider other ways to solve the problem.
6464+ - File a report with the upstream by following the instructions in
6565+ @{article:Contributing Bug Reports}.
6666+6767+The remaining sections in this document walk through these steps.
6868+6969+7070+Understanding Performance Problems
7171+==================================
7272+7373+To isolate, examine, and understand performance problems, follow these steps:
7474+7575+**General Slowness**: If you are experiencing generally poor performance, use
7676+Multimeter to understand resource usage and look for load-based causes. See
7777+@{article:Multimeter User Guide}. If that isn't fruitful, treat this like a
7878+reproducible performance problem on an arbitrary page.
7979+8080+**Hangs**: If you are experiencing hangs (pages which never return, or which
8181+time out with a fatal after some number of seconds), they are almost always
8282+the result of bugs in the upstream. Report them by following these
8383+instructions:
8484+8585+ - Set `debug.time-limit` to a value like `5`.
8686+ - Reproduce the hang. The page should exit after 5 seconds with a more useful
8787+ stack trace.
8888+ - File a report with the reproduction instructions and the stack trace in
8989+ the upstream. See @{article:Contributing Bug Reports} for detailed
9090+ instructions.
9191+ - Clear `debug.time-limit` again to take your install out of debug mode.
9292+9393+If part of the reproduction instructions include "Create a 100MB wiki page",
9494+the upstream may be less sympathetic to your cause than if reproducing the
9595+issue does not require an unusual, complex workload.
9696+9797+In some cases, the hang may really just a very large amount of processing time.
9898+If you're very excited about 100MB wiki pages and don't mind waiting many
9999+minutes for them to render, you may be able to adjust `max_execution_time` in
100100+your PHP configuration to allow the process enough time to complete, or adjust
101101+settings in your webserver config to let it wait longer for results.
102102+103103+**DarkConsole**: If you have a reproducible performance problem (for example,
104104+loading a specific page is very slow), you can enable DarkConsole (a builtin
105105+debugging console) to examine page performance in detail.
106106+107107+The two most useful tabs in DarkConsole are the "Services" tab and the
108108+"XHProf" tab.
109109+110110+The "Services" module allows you to examine service calls (network calls,
111111+subprocesses, events, etc) and find slow queries, slow services, inefficient
112112+query plans, and unnecessary calls. Broadly, you're looking for slow or
113113+repeated service calls, or calls which don't make sense given what the page
114114+should be doing.
115115+116116+After installing XHProf (see @{article:Using XHProf}) you'll gain access to the
117117+"XHProf" tab, which is a full tracing profiler. You can use the "Profile Page"
118118+button to generate a complete trace of where a page is spending time. When
119119+reading a profile, you're looking for the overall use of time, and for anything
120120+which sticks out as taking unreasonably long or not making sense.
121121+122122+See @{article:Using DarkConsole} for complete instructions on configuring
123123+and using DarkConsole.
124124+125125+**AJAX Requests**: To debug Ajax requests, activate DarkConsole and then turn
126126+on the profiler or query analyzer on the main request by clicking the
127127+appropriate button. The setting will cascade to Ajax requests made by the page
128128+and they'll show up in the console with full query analysis or profiling
129129+information.
130130+131131+**Command-Line Hangs**: If you have a script or daemon hanging, you can send
132132+it `SIGHUP` to have it dump a stack trace to `sys_get_temp_dir()` (usually
133133+`/tmp`).
134134+135135+Do this with:
136136+137137+```
138138+$ kill -HUP <pid>
139139+```
140140+141141+You can use this command to figure out where the system's temporary directory
142142+is:
143143+144144+```
145145+$ php -r 'echo sys_get_temp_dir()."\n";'
146146+```
147147+148148+On most systems, this is `/tmp`. The trace should appear in that directory with
149149+a name like `phabricator_backtrace_<pid>`. Examining this trace may provide
150150+a key to understanding the problem.
151151+152152+**Command-Line Performance**: If you have general performance issues with
153153+command-line scripts, you can add `--trace` to see a service call log. This is
154154+similar to the "Services" tab in DarkConsole. This may help identify issues.
155155+156156+After installing XHProf, you can also add `--xprofile <filename>` to emit a
157157+detailed performance profile. You can `arc upload` these files and then view
158158+them in XHProf from the web UI.
159159+160160+Next Steps
161161+==========
162162+163163+If you've done all you can to isolate and understand the problem you're
164164+experiencing, report it to the upstream. Including as much relevant data as
165165+you can, including:
166166+167167+ - reproduction instructions;
168168+ - traces from `debug.time-limit` for hangs;
169169+ - screenshots of service call logs from DarkConsole (review these carefully,
170170+ as they can sometimes contain sensitive information);
171171+ - traces from CLI scripts with `--trace`;
172172+ - traces from sending HUP to processes; and
173173+ - XHProf profile files from `--xprofile` or "Download .xhprof Profile" in
174174+ the web UI.
175175+176176+After collecting this information:
177177+178178+ - follow the instructions in @{article:Contributing Bug Reports} to file
179179+ a report in the upstream.
+122
src/docs/user/field/xhprof.diviner
···11+@title Using XHProf
22+@group fieldmanual
33+44+Describes how to install and use XHProf, a PHP profiling tool.
55+66+Overview
77+========
88+99+XHProf is a profiling tool which will let you understand application
1010+performance in Phabricator.
1111+1212+After you install XHProf, you can use it from the web UI and the CLI to
1313+generate detailed performance profiles. It is the most powerful tool available
1414+for understanding application performance and identifying and fixing slow code.
1515+1616+Installing XHProf
1717+=================
1818+1919+You are likely to have the most luck building XHProf from source:
2020+2121+ $ git clone https://github.com/phacility/xhprof.git
2222+2323+From any source distribution of the extension, build and install it like this:
2424+2525+ $ cd xhprof/
2626+ $ cd extension/
2727+ $ phpize
2828+ $ ./configure
2929+ $ make
3030+ $ sudo make install
3131+3232+You may also need to add `extension=xhprof.so` to your php.ini.
3333+3434+You can also try using PECL to install it, but this may not work well with
3535+recent versions of PHP:
3636+3737+ $ pecl install xhprof
3838+3939+Once you've installed it, `php -i` should report it as installed (you may
4040+see a different version number, which is fine):
4141+4242+ $ php -i | grep xhprof
4343+ ...
4444+ xhprof => 0.9.2
4545+ ...
4646+4747+4848+Using XHProf: Web UI
4949+====================
5050+5151+To profile a web page, activate DarkConsole and navigate to the XHProf tab.
5252+Use the **Profile Page** button to generate a profile.
5353+5454+For instructions on activating DarkConsole, see @{article:Using DarkConsole}.
5555+5656+5757+Using XHProf: CLI
5858+=================
5959+6060+From the command line, use the `--xprofile <filename>` flag to generate a
6161+profile of any script.
6262+6363+You can then upload this file to Phabricator (using `arc upload` may be easiest)
6464+and view it in the web UI.
6565+6666+6767+Analyzing Profiles
6868+==================
6969+7070+Understanding profiles is as much art as science, so be warned that you may not
7171+make much headway. Even if you aren't able to conclusively read a profile
7272+yourself, you can attach profiles when submitting bug reports to the upstream
7373+and we can look at them. This may yield new insight.
7474+7575+When looking at profiles, the "Wall Time (Inclusive)" column is usually the
7676+most important. This shows the total amount of time spent in a function or
7777+method and all of its children. Usually, to improve the performance of a page,
7878+we're trying to find something that's slow and make it not slow: this column
7979+can help identify which things are slowest.
8080+8181+The "Wall Time (Exclusive)" column shows time spent in a function or method,
8282+excluding time spent in its children. This can give you hint about whether the
8383+call itself is slow or it's just making calls to other things that are slow.
8484+8585+You can also get a sense of this by clicking a call to see its children, and
8686+seeing if the bulk of runtime is spent in a child call. This tends to indicate
8787+that you're looking at a problem which is deeper in the stack, and you need
8888+to go down further to identify and understand it.
8989+9090+Conversely, if the "Wall Time (Exclusive)" column is large, or the children
9191+of a call are all cheap, there's probably something expesive happening in the
9292+call itself.
9393+9494+The "Count" column can also sometimes tip you off that something is amiss, if
9595+a method which shouldn't be called very often is being called a lot.
9696+9797+Some general thing to look for -- these aren't smoking guns, but are unusual
9898+and can lead to finding a performance issue:
9999+100100+ - Is a low-level utility method like `phutil_utf8ize()` or `array_merge()`
101101+ taking more than a few percent of the page runtime?
102102+ - Do any methods (especially high-level methods) have >10,00 calls?
103103+ - Are we spending more than 100ms doing anything which isn't loading data
104104+ or rendering data?
105105+ - Does anything look suspiciously expensive or out of place?
106106+ - Is the profile for the slow page a lot different than the profile for a
107107+ fast page?
108108+109109+Some performance problems are obvious and will jump out of a profile; others
110110+may require a more nuanced understanding of the codebase to sniff out which
111111+parts are suspicious. If you aren't able to make progress with a profile,
112112+report the issue upstream and attach the profile to your report.
113113+114114+115115+Next Steps
116116+==========
117117+118118+Continue by:
119119+120120+ - enabling DarkConsole with @{article:Using DarkConsole}; or
121121+ - understanding and reporting performance problems with
122122+ @{article:Troubleshooting Performance Problems}.
···38383939If you haven't created a library for the class to live in yet, you need to do
4040that first. Follow the instructions in
4141-@{article:libphutil Libraries User Guide}, then make the library loadable by
4141+@{article@contributor:Adding New Classes}, then make the library loadable by
4242adding it to your `.arcconfig` like this:
43434444 {
···47474848 - **load**: list of additional Phutil libraries to load at startup.
4949 See below for details about path resolution, or see
5050- @{article:libphutil Libraries User Guide} for a general introduction to
5050+ @{article@contributor:Adding New Classes} for a general introduction to
5151 libphutil libraries.
5252 - **https.cabundle**: specifies the path to an alternate certificate bundle
5353 for use when making HTTPS connections.
+2-2
src/docs/user/userguide/events.diviner
···21212222 - Write a listener class which extends @{class@libphutil:PhutilEventListener}.
2323 - Add it to a libphutil library, or create a new library (for instructions,
2424- see @{article:libphutil Libraries User Guide}.
2424+ see @{article@contributor:Adding New Classes}.
2525 - Configure Phabricator to load the library by adding it to `load-libraries`
2626 in the Phabricator config.
2727 - Configure Phabricator to install the event listener by adding the class
···38383939 - Write a listener class which extends @{class@libphutil:PhutilEventListener}.
4040 - Add it to a libphutil library, or create a new library (for instructions,
4141- see @{article:libphutil Libraries User Guide}.
4141+ see @{article@contributor:Adding New Classes}.
4242 - Configure Phabricator to load the library by adding it to `load`
4343 in the Arcanist config (e.g., `.arcconfig`, or user/global config).
4444 - Configure Arcanist to install the event listener by adding the class
-155
src/docs/user/userguide/libraries.diviner
···11-@title libphutil Libraries User Guide
22-@group userguide
33-44-Guide to creating and managing libphutil libraries.
55-66-= Overview =
77-88-libphutil includes a library system which organizes PHP classes and functions
99-into modules. Some extensions and customizations of Arcanist and Phabricator
1010-require you to make code available to Phabricator by providing it in a libphutil
1111-library.
1212-1313-For example, if you want to store files in some kind of custom storage engine,
1414-you need to write a class which can interact with that engine and then tell
1515-Phabricator to load it.
1616-1717-In general, you perform these one-time setup steps:
1818-1919- - Create a new directory.
2020- - Use `arc liberate` to initialize and name the library.
2121- - Add a dependency on Phabricator if necessary.
2222- - Add the library to your Phabricator config or `.arcconfig` so it will be
2323- loaded at runtime.
2424-2525-Then, to add new code, you do this:
2626-2727- - Write or update classes.
2828- - Update the library metadata by running `arc liberate` again.
2929-3030-= Creating a New Library =
3131-3232-To **create a new libphutil library**:
3333-3434- $ mkdir libcustom/
3535- $ cd libcustom/
3636- libcustom/ $ arc liberate src/
3737-3838-Now you'll get a prompt like this:
3939-4040- lang=txt
4141- No library currently exists at that path...
4242- The directory '/some/path/libcustom/src' does not exist.
4343-4444- Do you want to create it? [y/N] y
4545- Creating new libphutil library in '/some/path/libcustom/src'.
4646- Choose a name for the new library.
4747-4848- What do you want to name this library?
4949-5050-Choose a library name (in this case, "libcustom" would be appropriate) and it
5151-you should get some details about the library initialization:
5252-5353- lang=txt
5454- Writing '__phutil_library_init__.php' to
5555- '/some/path/libcustom/src/__phutil_library_init__.php'...
5656- Using library root at 'src'...
5757- Mapping library...
5858- Verifying library...
5959- Finalizing library map...
6060- OKAY Library updated.
6161-6262-This will write three files:
6363-6464- - `src/.phutil_module_cache` This is a cache which makes "arc liberate"
6565- faster when you run it to update the library. You can safely remove it at
6666- any time. If you check your library into version control, you can add this
6767- file to ignore rules (like .gitignore).
6868- - `src/__phutil_library_init__.php` This records the name of the library and
6969- tells libphutil that a library exists here.
7070- - `src/__phutil_library_map__.php` This is a map of all the symbols
7171- (functions and classes) in the library, which allows them to be autoloaded
7272- at runtime and dependencies to be statically managed by "arc liberate".
7373-7474-= Linking with Phabricator =
7575-7676-If you aren't using this library with Phabricator (e.g., you are only using it
7777-with Arcanist or are building something else on libphutil) you can skip this
7878-step.
7979-8080-But, if you intend to use this library with Phabricator, you need to define its
8181-dependency on Phabricator by creating a `.arcconfig` file which points at
8282-Phabricator. For example, you might write this file to
8383-`libcustom/.arcconfig`:
8484-8585- {
8686- "load": [
8787- "phabricator/src/"
8888- ]
8989- }
9090-9191-For details on creating a `.arcconfig`, see
9292-@{article:Arcanist User Guide: Configuring a New Project}. In general, this
9393-tells `arc liberate` that it should look for symbols in Phabricator when
9494-performing static analysis.
9595-9696-NOTE: If Phabricator isn't located next to your custom library, specify a
9797-path which actually points to the `phabricator/` directory.
9898-9999-You do not need to declare dependencies on `arcanist` or `libphutil`,
100100-since `arc liberate` automatically loads them.
101101-102102-Finally, edit your Phabricator config to tell it to load your library at
103103-runtime, by adding it to `load-libraries`:
104104-105105- ...
106106- 'load-libraries' => array(
107107- 'libcustom' => 'libcustom/src/',
108108- ),
109109- ...
110110-111111-Now, Phabricator will be able to load classes from your custom library.
112112-113113-= Writing Classes =
114114-115115-To actually write classes, create a new module and put code in it:
116116-117117- libcustom/ $ mkdir src/example/
118118- libcustom/ $ nano src/example/ExampleClass.php # Edit some code.
119119-120120-Now, run `arc liberate` to regenerate the static resource map:
121121-122122- libcustom/ $ arc liberate src/
123123-124124-This will automatically regenerate the static map of the library.
125125-126126-= What You Can Extend And Invoke =
127127-128128-libphutil, Arcanist and Phabricator are strict about extensibility of classes
129129-and visibility of methods and properties. Most classes are marked `final`, and
130130-methods have the minimum required visibility (protected or private). The goal of
131131-this strictness is to make it clear what you can safely extend, access, and
132132-invoke, so your code will keep working as the upstream changes.
133133-134134-When developing libraries to work with libphutil, Arcanist and Phabricator, you
135135-should respect method and property visibility and extend only classes marked
136136-`@stable`. They are rendered with a large callout in the documentation (for
137137-example: @{class@libphutil:AbstractDirectedGraph}). These classes are external
138138-interfaces intended for extension.
139139-140140-If you want to extend a class but it is not marked `@stable`, here are some
141141-approaches you can take:
142142-143143- - Good: If possible, use composition rather than extension to build your
144144- feature.
145145- - Good: Check the documentation for a better way to accomplish what you're
146146- trying to do.
147147- - Good: Let us know what your use case is so we can make the class tree more
148148- flexible or configurable, or point you at the right way to do whatever
149149- you're trying to do, or explain why we don't let you do it.
150150- - Discouraged: Send us a patch removing "final" (or turning "protected" or
151151- "private" into "public"). We generally will not accept these patches, unless
152152- there's a good reason that the current behavior is wrong.
153153- - Discouraged: Create an ad-hoc local fork and remove "final" in your copy of
154154- the code. This will make it more difficult for you to upgrade in the future.
155155- - Discouraged: Use Reflection to violate visibility keywords.
+99
src/docs/user/userguide/multimeter.diviner
···11+@title Multimeter User Guide
22+@group userguide
33+44+Using Multimeter, a sampling profiler.
55+66+Overview
77+========
88+99+IMPORTANT: This document describes a prototype application.
1010+1111+Multimeter is a sampling profiler that can give you coarse information about
1212+Phabricator resource usage. In particular, it can help quickly identify sources
1313+of load, like bots or scripts which are making a very large number of requests.
1414+1515+Configuring and Using Multimeter
1616+================================
1717+1818+To access Multimeter, go to {nav Applications > Multimeter}.
1919+2020+By default, Multimeter samples 0.1% of pages. This should be a reasonable rate
2121+for most installs, but you can increase or decrease the rate by adjusting
2222+`debug.sample-rate`. Increasing the rate (by setting the value to a lower
2323+number, like 100, to sample 1% of pages) will increase the granualrity of the
2424+data, at a small performance cost.
2525+2626+Using Multimeter
2727+================
2828+2929+Multimeter shows you what Phabricator has spent time doing recently. By
3030+looking at the samples it collects, you can identify major sources of load
3131+or resource use, whether they are specific users, pages, subprocesses, or
3232+other types of activity.
3333+3434+By identifying and understanding unexpected load, you can adjust usage patterns
3535+or configuration to make better use of resources (for example, rewrite bots
3636+that are making too many calls), or report specific, actionable issues to the
3737+upstream for resolution.
3838+3939+The main screen of Multimeter shows you everything Phabricator has spent
4040+resources on recently, broken down by action type. Categories are folded up
4141+by default, with "(All)" labels.
4242+4343+To filter by a dimension, click the link for it. For example, from the main
4444+page, you can click "Web Request" to filter by only web requests. To expand a
4545+grouped dimension, click the "(All)" link.
4646+4747+For example, suppose we suspect that someone is running a bot that is making
4848+a lot of requests and consuming a lot of resources. We can get a better idea
4949+about this by filtering the results like this:
5050+5151+ - Click {nav Web Request}. This will show only web requests.
5252+ - Click {nav (All)} under "Viewer". This will expand events by viewer.
5353+5454+Recent resource costs for web requests are now shown, grouped and sorted by
5555+user. The usernames in the "Viewer" column show who is using resources, in
5656+order from greatest use to least use (only administrators can see usernames).
5757+5858+The "Avg" column shows the average cost per event, while the "Cost" column
5959+shows the total cost.
6060+6161+If the top few users account for similar costs and are normal, active users,
6262+there may be nothing amiss and your problem might lie elsewhere. If a user like
6363+`slowbot` is in the top few users and has way higher usage than anyone else,
6464+there might be a script running under that account consuming a disproportionate
6565+amount of resources.
6666+6767+Assuming you find a user with unusual usage, you could dig into their usage
6868+like this:
6969+7070+ - Click their name (like {nav slowbot}) to filter to just their requests.
7171+ - Click {nav (All)} under "Label". This expands by request detail.
7272+7373+This will show exactly what they spent those resources doing, and can help
7474+identify if they're making a lot of API calls or scraping the site or whatever
7575+else.
7676+7777+This is just an example of a specific kind of problem that Multimeter could
7878+help resolve. In general, exploring Multimeter data by filtering and expanding
7979+resource uses can help you understand how resources are used and identify
8080+unexpected uses of resources. For example:
8181+8282+ - Identify a problem with load balancing by filtering on {nav Web Request}
8383+ and expanding on {nav Host}. If hosts aren't roughly even, DNS or a load
8484+ balancer are misconfigured.
8585+ - Identify which pages cost the most by filtering on {nav Web Request}
8686+ and expanding on {nav Label}.
8787+ - Find outlier pages by filtering on {nav Web Request} and expanding on
8888+ {nav ID}.
8989+ - Find where subprocess are invoked from by filtering on {nav Subprocesses},
9090+ then expanding on {nav Context}.
9191+9292+9393+Next Steps
9494+==========
9595+9696+Continue by:
9797+9898+ - understanding and reporting performance issues with
9999+ @{article:Troubleshooting Performance Problems}.