1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include "php_grpc.h"
20 
21 #include "call.h"
22 #include "channel.h"
23 #include "server.h"
24 #include "timeval.h"
25 #include "version.h"
26 #include "channel_credentials.h"
27 #include "call_credentials.h"
28 #include "server_credentials.h"
29 #include "completion_queue.h"
30 #include <inttypes.h>
31 #include <grpc/support/alloc.h>
32 #include <grpc/support/log.h>
33 #include <grpc/support/string_util.h>
34 #include <grpc/support/time.h>
35 #include <ext/spl/spl_exceptions.h>
36 #include <zend_exceptions.h>
37 
38 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
39 #include <pthread.h>
40 #endif
41 
42 ZEND_DECLARE_MODULE_GLOBALS(grpc)
43 static PHP_GINIT_FUNCTION(grpc);
44 HashTable grpc_persistent_list;
45 HashTable grpc_target_upper_bound_map;
46 /* {{{ grpc_functions[]
47  *
48  * Every user visible function must have an entry in grpc_functions[].
49  */
50 const zend_function_entry grpc_functions[] = {
51     PHP_FE_END /* Must be the last line in grpc_functions[] */
52 };
53 /* }}} */
54 
55 ZEND_DECLARE_MODULE_GLOBALS(grpc);
56 
57 /* {{{ grpc_module_entry
58  */
59 zend_module_entry grpc_module_entry = {
60   STANDARD_MODULE_HEADER,
61   "grpc",
62   grpc_functions,
63   PHP_MINIT(grpc),
64   PHP_MSHUTDOWN(grpc),
65   PHP_RINIT(grpc),
66   NULL,
67   PHP_MINFO(grpc),
68   PHP_GRPC_VERSION,
69   PHP_MODULE_GLOBALS(grpc),
70   PHP_GINIT(grpc),
71   NULL,
72   NULL,
73   STANDARD_MODULE_PROPERTIES_EX};
74 /* }}} */
75 
76 #ifdef COMPILE_DL_GRPC
77 ZEND_GET_MODULE(grpc)
78 #endif
79 
80 /* {{{ PHP_INI
81  */
PHP_INI_BEGIN()82    PHP_INI_BEGIN()
83    STD_PHP_INI_ENTRY("grpc.enable_fork_support", "0", PHP_INI_SYSTEM, OnUpdateBool,
84                      enable_fork_support, zend_grpc_globals, grpc_globals)
85    STD_PHP_INI_ENTRY("grpc.poll_strategy", NULL, PHP_INI_SYSTEM, OnUpdateString,
86                      poll_strategy, zend_grpc_globals, grpc_globals)
87    STD_PHP_INI_ENTRY("grpc.grpc_verbosity", NULL, PHP_INI_SYSTEM, OnUpdateString,
88                      grpc_verbosity, zend_grpc_globals, grpc_globals)
89    STD_PHP_INI_ENTRY("grpc.grpc_trace", NULL, PHP_INI_SYSTEM, OnUpdateString,
90                      grpc_trace, zend_grpc_globals, grpc_globals)
91    STD_PHP_INI_ENTRY("grpc.log_filename", NULL, PHP_INI_SYSTEM, OnUpdateString,
92                      log_filename, zend_grpc_globals, grpc_globals)
93    PHP_INI_END()
94 /* }}} */
95 
96 /* {{{ php_grpc_init_globals
97  */
98 /* Uncomment this function if you have INI entries
99    static void php_grpc_init_globals(zend_grpc_globals *grpc_globals)
100    {
101      grpc_globals->global_value = 0;
102      grpc_globals->global_string = NULL;
103    }
104 */
105 /* }}} */
106 
107 void create_new_channel(
108     wrapped_grpc_channel *channel,
109     char *target,
110     grpc_channel_args args,
111     wrapped_grpc_channel_credentials *creds) {
112   if (creds == NULL) {
113     channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args,
114                                                              NULL);
115   } else {
116     channel->wrapper->wrapped =
117         grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
118   }
119 }
120 
acquire_persistent_locks()121 void acquire_persistent_locks() {
122   zval *data;
123   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
124     php_grpc_zend_resource *rsrc  =
125                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
126     if (rsrc == NULL) {
127       break;
128     }
129     channel_persistent_le_t* le = rsrc->ptr;
130 
131     gpr_mu_lock(&le->channel->mu);
132   PHP_GRPC_HASH_FOREACH_END()
133 }
134 
release_persistent_locks()135 void release_persistent_locks() {
136   zval *data;
137   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
138     php_grpc_zend_resource *rsrc  =
139                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
140     if (rsrc == NULL) {
141       break;
142     }
143     channel_persistent_le_t* le = rsrc->ptr;
144 
145     gpr_mu_unlock(&le->channel->mu);
146   PHP_GRPC_HASH_FOREACH_END()
147 }
148 
destroy_grpc_channels()149 void destroy_grpc_channels() {
150   zval *data;
151   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
152     php_grpc_zend_resource *rsrc  =
153                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
154     if (rsrc == NULL) {
155       break;
156     }
157     channel_persistent_le_t* le = rsrc->ptr;
158 
159     wrapped_grpc_channel wrapped_channel;
160     wrapped_channel.wrapper = le->channel;
161     grpc_channel_wrapper *channel = wrapped_channel.wrapper;
162     grpc_channel_destroy(channel->wrapped);
163     channel->wrapped = NULL;
164   PHP_GRPC_HASH_FOREACH_END()
165 }
166 
prefork()167 void prefork() {
168   acquire_persistent_locks();
169 }
170 
171 // Clean all channels in the persistent list
172 // Called at post fork
php_grpc_clean_persistent_list(TSRMLS_D)173 void php_grpc_clean_persistent_list(TSRMLS_D) {
174     zend_hash_clean(&grpc_persistent_list);
175     zend_hash_clean(&grpc_target_upper_bound_map);
176 }
177 
postfork_child()178 void postfork_child() {
179   TSRMLS_FETCH();
180 
181   // loop through persistent list and destroy all underlying grpc_channel objs
182   destroy_grpc_channels();
183 
184   release_persistent_locks();
185 
186   // clean all channels in the persistent list
187   php_grpc_clean_persistent_list(TSRMLS_C);
188 
189   // clear completion queue
190   grpc_php_shutdown_completion_queue(TSRMLS_C);
191 
192   // clean-up grpc_core
193   grpc_shutdown();
194   if (grpc_is_initialized() > 0) {
195     zend_throw_exception(spl_ce_UnexpectedValueException,
196                          "Oops, failed to shutdown gRPC Core after fork()",
197                          1 TSRMLS_CC);
198   }
199 
200   // restart grpc_core
201   grpc_init();
202   grpc_php_init_completion_queue(TSRMLS_C);
203 }
204 
postfork_parent()205 void postfork_parent() {
206   release_persistent_locks();
207 }
208 
register_fork_handlers()209 void register_fork_handlers() {
210   if (getenv("GRPC_ENABLE_FORK_SUPPORT")) {
211 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
212     pthread_atfork(&prefork, &postfork_parent, &postfork_child);
213 #endif  // GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
214   }
215 }
216 
apply_ini_settings(TSRMLS_D)217 void apply_ini_settings(TSRMLS_D) {
218   if (GRPC_G(enable_fork_support)) {
219     char *enable_str = malloc(sizeof("GRPC_ENABLE_FORK_SUPPORT=1"));
220     strcpy(enable_str, "GRPC_ENABLE_FORK_SUPPORT=1");
221     putenv(enable_str);
222   }
223 
224   if (GRPC_G(poll_strategy)) {
225     char *poll_str = malloc(sizeof("GRPC_POLL_STRATEGY=") +
226                             strlen(GRPC_G(poll_strategy)));
227     strcpy(poll_str, "GRPC_POLL_STRATEGY=");
228     strcat(poll_str, GRPC_G(poll_strategy));
229     putenv(poll_str);
230   }
231 
232   if (GRPC_G(grpc_verbosity)) {
233     char *verbosity_str = malloc(sizeof("GRPC_VERBOSITY=") +
234                                  strlen(GRPC_G(grpc_verbosity)));
235     strcpy(verbosity_str, "GRPC_VERBOSITY=");
236     strcat(verbosity_str, GRPC_G(grpc_verbosity));
237     putenv(verbosity_str);
238   }
239 
240   if (GRPC_G(grpc_trace)) {
241     char *trace_str = malloc(sizeof("GRPC_TRACE=") +
242                              strlen(GRPC_G(grpc_trace)));
243     strcpy(trace_str, "GRPC_TRACE=");
244     strcat(trace_str, GRPC_G(grpc_trace));
245     putenv(trace_str);
246   }
247 }
248 
custom_logger(gpr_log_func_args * args)249 static void custom_logger(gpr_log_func_args* args) {
250   TSRMLS_FETCH();
251 
252   const char* final_slash;
253   const char* display_file;
254   char* prefix;
255   char* final;
256   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
257 
258   final_slash = strrchr(args->file, '/');
259   if (final_slash) {
260     display_file = final_slash + 1;
261   } else {
262     display_file = args->file;
263   }
264 
265   FILE *fp = fopen(GRPC_G(log_filename), "ab");
266   if (!fp) {
267     return;
268   }
269 
270   gpr_asprintf(&prefix, "%s%" PRId64 ".%09" PRId32 " %s:%d]",
271                gpr_log_severity_string(args->severity), now.tv_sec,
272                now.tv_nsec, display_file, args->line);
273 
274   gpr_asprintf(&final, "%-60s %s\n", prefix, args->message);
275 
276   fprintf(fp, "%s", final);
277   fclose(fp);
278   gpr_free(prefix);
279   gpr_free(final);
280 }
281 
282 /* {{{ PHP_MINIT_FUNCTION
283  */
PHP_MINIT_FUNCTION(grpc)284 PHP_MINIT_FUNCTION(grpc) {
285   REGISTER_INI_ENTRIES();
286 
287   /* Register call error constants */
288   /** everything went ok */
289   REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK,
290                          CONST_CS | CONST_PERSISTENT);
291   /** something failed, we don't know what */
292   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR,
293                          CONST_CS | CONST_PERSISTENT);
294   /** this method is not available on the server */
295   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER",
296                          GRPC_CALL_ERROR_NOT_ON_SERVER,
297                          CONST_CS | CONST_PERSISTENT);
298   /** this method is not available on the client */
299   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT",
300                          GRPC_CALL_ERROR_NOT_ON_CLIENT,
301                          CONST_CS | CONST_PERSISTENT);
302   /** this method must be called before invoke */
303   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED",
304                          GRPC_CALL_ERROR_ALREADY_INVOKED,
305                          CONST_CS | CONST_PERSISTENT);
306   /** this method must be called after invoke */
307   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED",
308                          GRPC_CALL_ERROR_NOT_INVOKED,
309                          CONST_CS | CONST_PERSISTENT);
310   /** this call is already finished
311       (writes_done or write_status has already been called) */
312   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED",
313                          GRPC_CALL_ERROR_ALREADY_FINISHED,
314                          CONST_CS | CONST_PERSISTENT);
315   /** there is already an outstanding read/write operation on the call */
316   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS",
317                          GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
318                          CONST_CS | CONST_PERSISTENT);
319   /** the flags value was illegal for this call */
320   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS",
321                          GRPC_CALL_ERROR_INVALID_FLAGS,
322                          CONST_CS | CONST_PERSISTENT);
323 
324   /* Register flag constants */
325   /** Hint that the write may be buffered and need not go out on the wire
326       immediately. GRPC is free to buffer the message until the next non-buffered
327       write, or until writes_done, but it need not buffer completely or at all. */
328   REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT,
329                          CONST_CS | CONST_PERSISTENT);
330   /** Force compression to be disabled for a particular write
331       (start_write/add_metadata). Illegal on invoke/accept. */
332   REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS,
333                          CONST_CS | CONST_PERSISTENT);
334 
335   /* Register status constants */
336   /** Not an error; returned on success */
337   REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK,
338                          CONST_CS | CONST_PERSISTENT);
339   /** The operation was cancelled (typically by the caller). */
340   REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED,
341                          CONST_CS | CONST_PERSISTENT);
342   /** Unknown error.  An example of where this error may be returned is
343       if a Status value received from another address space belongs to
344       an error-space that is not known in this address space.  Also
345       errors raised by APIs that do not return enough error information
346       may be converted to this error. */
347   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN,
348                          CONST_CS | CONST_PERSISTENT);
349   /** Client specified an invalid argument.  Note that this differs
350       from FAILED_PRECONDITION.  INVALID_ARGUMENT indicates arguments
351       that are problematic regardless of the state of the system
352       (e.g., a malformed file name). */
353   REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT",
354                          GRPC_STATUS_INVALID_ARGUMENT,
355                          CONST_CS | CONST_PERSISTENT);
356   /** Deadline expired before operation could complete.  For operations
357       that change the state of the system, this error may be returned
358       even if the operation has completed successfully.  For example, a
359       successful response from a server could have been delayed long
360       enough for the deadline to expire. */
361   REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED",
362                          GRPC_STATUS_DEADLINE_EXCEEDED,
363                          CONST_CS | CONST_PERSISTENT);
364   /** Some requested entity (e.g., file or directory) was not found. */
365   REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND,
366                          CONST_CS | CONST_PERSISTENT);
367   /** Some entity that we attempted to create (e.g., file or directory)
368       already exists. */
369   REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS",
370                          GRPC_STATUS_ALREADY_EXISTS,
371                          CONST_CS | CONST_PERSISTENT);
372   /** The caller does not have permission to execute the specified
373       operation.  PERMISSION_DENIED must not be used for rejections
374       caused by exhausting some resource (use RESOURCE_EXHAUSTED
375       instead for those errors).  PERMISSION_DENIED must not be
376       used if the caller can not be identified (use UNAUTHENTICATED
377       instead for those errors). */
378   REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED",
379                          GRPC_STATUS_PERMISSION_DENIED,
380                          CONST_CS | CONST_PERSISTENT);
381   /** The request does not have valid authentication credentials for the
382       operation. */
383   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED",
384                          GRPC_STATUS_UNAUTHENTICATED,
385                          CONST_CS | CONST_PERSISTENT);
386   /** Some resource has been exhausted, perhaps a per-user quota, or
387       perhaps the entire file system is out of space. */
388   REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED",
389                          GRPC_STATUS_RESOURCE_EXHAUSTED,
390                          CONST_CS | CONST_PERSISTENT);
391   /** Operation was rejected because the system is not in a state
392       required for the operation's execution.  For example, directory
393       to be deleted may be non-empty, an rmdir operation is applied to
394       a non-directory, etc.
395 
396       A litmus test that may help a service implementor in deciding
397       between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
398        (a) Use UNAVAILABLE if the client can retry just the failing call.
399        (b) Use ABORTED if the client should retry at a higher-level
400            (e.g., restarting a read-modify-write sequence).
401        (c) Use FAILED_PRECONDITION if the client should not retry until
402            the system state has been explicitly fixed.  E.g., if an "rmdir"
403            fails because the directory is non-empty, FAILED_PRECONDITION
404            should be returned since the client should not retry unless
405            they have first fixed up the directory by deleting files from it.
406        (d) Use FAILED_PRECONDITION if the client performs conditional
407            REST Get/Update/Delete on a resource and the resource on the
408            server does not match the condition. E.g., conflicting
409            read-modify-write on the same resource. */
410   REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION",
411                          GRPC_STATUS_FAILED_PRECONDITION,
412                          CONST_CS | CONST_PERSISTENT);
413   /** The operation was aborted, typically due to a concurrency issue
414       like sequencer check failures, transaction aborts, etc.
415 
416       See litmus test above for deciding between FAILED_PRECONDITION,
417       ABORTED, and UNAVAILABLE. */
418   REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED,
419                          CONST_CS | CONST_PERSISTENT);
420   /** Operation was attempted past the valid range.  E.g., seeking or
421       reading past end of file.
422 
423       Unlike INVALID_ARGUMENT, this error indicates a problem that may
424       be fixed if the system state changes. For example, a 32-bit file
425       system will generate INVALID_ARGUMENT if asked to read at an
426       offset that is not in the range [0,2^32-1], but it will generate
427       OUT_OF_RANGE if asked to read from an offset past the current
428       file size.
429 
430       There is a fair bit of overlap between FAILED_PRECONDITION and
431       OUT_OF_RANGE.  We recommend using OUT_OF_RANGE (the more specific
432       error) when it applies so that callers who are iterating through
433       a space can easily look for an OUT_OF_RANGE error to detect when
434       they are done. */
435   REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE",
436                          GRPC_STATUS_OUT_OF_RANGE,
437                          CONST_CS | CONST_PERSISTENT);
438   /** Operation is not implemented or not supported/enabled in this service. */
439   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED",
440                          GRPC_STATUS_UNIMPLEMENTED,
441                          CONST_CS | CONST_PERSISTENT);
442   /** Internal errors.  Means some invariants expected by underlying
443       system has been broken.  If you see one of these errors,
444       something is very broken. */
445   REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL,
446                          CONST_CS | CONST_PERSISTENT);
447   /** The service is currently unavailable.  This is a most likely a
448       transient condition and may be corrected by retrying with
449       a backoff. Note that it is not always safe to retry non-idempotent
450       operations.
451 
452       WARNING: Although data MIGHT not have been transmitted when this
453       status occurs, there is NOT A GUARANTEE that the server has not seen
454       anything. So in general it is unsafe to retry on this status code
455       if the call is non-idempotent.
456 
457       See litmus test above for deciding between FAILED_PRECONDITION,
458       ABORTED, and UNAVAILABLE. */
459   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE,
460                          CONST_CS | CONST_PERSISTENT);
461   /** Unrecoverable data loss or corruption. */
462   REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS,
463                          CONST_CS | CONST_PERSISTENT);
464 
465   /* Register op type constants */
466   /** Send initial metadata: one and only one instance MUST be sent for each
467       call, unless the call was cancelled - in which case this can be skipped.
468       This op completes after all bytes of metadata have been accepted by
469       outgoing flow control. */
470   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_INITIAL_METADATA",
471                          GRPC_OP_SEND_INITIAL_METADATA,
472                          CONST_CS | CONST_PERSISTENT);
473   /** Send a message: 0 or more of these operations can occur for each call.
474       This op completes after all bytes for the message have been accepted by
475       outgoing flow control. */
476   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_MESSAGE",
477                          GRPC_OP_SEND_MESSAGE,
478                          CONST_CS | CONST_PERSISTENT);
479   /** Send a close from the client: one and only one instance MUST be sent from
480       the client, unless the call was cancelled - in which case this can be
481       skipped. This op completes after all bytes for the call
482       (including the close) have passed outgoing flow control. */
483   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_CLOSE_FROM_CLIENT",
484                          GRPC_OP_SEND_CLOSE_FROM_CLIENT,
485                          CONST_CS | CONST_PERSISTENT);
486   /** Send status from the server: one and only one instance MUST be sent from
487       the server unless the call was cancelled - in which case this can be
488       skipped. This op completes after all bytes for the call
489       (including the status) have passed outgoing flow control. */
490   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_STATUS_FROM_SERVER",
491                          GRPC_OP_SEND_STATUS_FROM_SERVER,
492                          CONST_CS | CONST_PERSISTENT);
493   /** Receive initial metadata: one and only one MUST be made on the client,
494       must not be made on the server.
495       This op completes after all initial metadata has been read from the
496       peer. */
497   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_INITIAL_METADATA",
498                          GRPC_OP_RECV_INITIAL_METADATA,
499                          CONST_CS | CONST_PERSISTENT);
500   /** Receive a message: 0 or more of these operations can occur for each call.
501       This op completes after all bytes of the received message have been
502       read, or after a half-close has been received on this call. */
503   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE",
504                          GRPC_OP_RECV_MESSAGE,
505                          CONST_CS | CONST_PERSISTENT);
506   /** Receive status on the client: one and only one must be made on the client.
507       This operation always succeeds, meaning ops paired with this operation
508       will also appear to succeed, even though they may not have. In that case
509       the status will indicate some failure.
510       This op completes after all activity on the call has completed. */
511   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT",
512                          GRPC_OP_RECV_STATUS_ON_CLIENT,
513                          CONST_CS | CONST_PERSISTENT);
514   /** Receive close on the server: one and only one must be made on the
515       server. This op completes after the close has been received by the
516       server. This operation always succeeds, meaning ops paired with
517       this operation will also appear to succeed, even though they may not
518       have. */
519   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_CLOSE_ON_SERVER",
520                          GRPC_OP_RECV_CLOSE_ON_SERVER,
521                          CONST_CS | CONST_PERSISTENT);
522 
523   /* Register connectivity state constants */
524   /** channel is idle */
525   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE",
526                          GRPC_CHANNEL_IDLE,
527                          CONST_CS | CONST_PERSISTENT);
528   /** channel is connecting */
529   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING",
530                          GRPC_CHANNEL_CONNECTING,
531                          CONST_CS | CONST_PERSISTENT);
532   /** channel is ready for work */
533   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY",
534                          GRPC_CHANNEL_READY,
535                          CONST_CS | CONST_PERSISTENT);
536   /** channel has seen a failure but expects to recover */
537   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE",
538                          GRPC_CHANNEL_TRANSIENT_FAILURE,
539                          CONST_CS | CONST_PERSISTENT);
540   /** channel has seen a failure that it cannot recover from */
541   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_FATAL_FAILURE",
542                          GRPC_CHANNEL_SHUTDOWN,
543                          CONST_CS | CONST_PERSISTENT);
544 
545   /** grpc version string */
546   REGISTER_STRING_CONSTANT("Grpc\\VERSION", PHP_GRPC_VERSION,
547                            CONST_CS | CONST_PERSISTENT);
548 
549   grpc_init_call(TSRMLS_C);
550   GRPC_STARTUP(channel);
551   grpc_init_server(TSRMLS_C);
552   grpc_init_timeval(TSRMLS_C);
553   grpc_init_channel_credentials(TSRMLS_C);
554   grpc_init_call_credentials(TSRMLS_C);
555   grpc_init_server_credentials(TSRMLS_C);
556   return SUCCESS;
557 }
558 /* }}} */
559 
560 /* {{{ PHP_MSHUTDOWN_FUNCTION
561  */
PHP_MSHUTDOWN_FUNCTION(grpc)562 PHP_MSHUTDOWN_FUNCTION(grpc) {
563   UNREGISTER_INI_ENTRIES();
564   // WARNING: This function IS being called by PHP when the extension
565   // is unloaded but the logs were somehow suppressed.
566   if (GRPC_G(initialized)) {
567     zend_hash_clean(&grpc_persistent_list);
568     zend_hash_destroy(&grpc_persistent_list);
569     zend_hash_clean(&grpc_target_upper_bound_map);
570     zend_hash_destroy(&grpc_target_upper_bound_map);
571     grpc_shutdown_timeval(TSRMLS_C);
572     grpc_php_shutdown_completion_queue(TSRMLS_C);
573     grpc_shutdown();
574     GRPC_G(initialized) = 0;
575   }
576   return SUCCESS;
577 }
578 /* }}} */
579 
580 /* {{{ PHP_MINFO_FUNCTION
581  */
PHP_MINFO_FUNCTION(grpc)582 PHP_MINFO_FUNCTION(grpc) {
583   php_info_print_table_start();
584   php_info_print_table_row(2, "grpc support", "enabled");
585   php_info_print_table_row(2, "grpc module version", PHP_GRPC_VERSION);
586   php_info_print_table_end();
587   DISPLAY_INI_ENTRIES();
588 }
589 /* }}} */
590 
591 /* {{{ PHP_RINIT_FUNCTION
592  */
PHP_RINIT_FUNCTION(grpc)593 PHP_RINIT_FUNCTION(grpc) {
594   if (!GRPC_G(initialized)) {
595     apply_ini_settings(TSRMLS_C);
596     if (GRPC_G(log_filename)) {
597       gpr_set_log_function(custom_logger);
598     }
599     grpc_init();
600     register_fork_handlers();
601     grpc_php_init_completion_queue(TSRMLS_C);
602     GRPC_G(initialized) = 1;
603   }
604   return SUCCESS;
605 }
606 /* }}} */
607 
608 /* {{{ PHP_GINIT_FUNCTION
609  */
PHP_GINIT_FUNCTION(grpc)610 static PHP_GINIT_FUNCTION(grpc) {
611   grpc_globals->initialized = 0;
612   grpc_globals->enable_fork_support = 0;
613   grpc_globals->poll_strategy = NULL;
614   grpc_globals->grpc_verbosity = NULL;
615   grpc_globals->grpc_trace = NULL;
616   grpc_globals->log_filename = NULL;
617 }
618 /* }}} */
619 
620 /* The previous line is meant for vim and emacs, so it can correctly fold and
621    unfold functions in source code. See the corresponding marks just before
622    function definition, where the functions purpose is also documented. Please
623    follow this convention for the convenience of others editing your code.
624 */
625