Skip to content

Commit d07c647

Browse files
committed
refactor some more
Extract all composer handling code from php_execute into its own module.
1 parent 9e6126c commit d07c647

File tree

4 files changed

+261
-220
lines changed

4 files changed

+261
-220
lines changed

agent/config.m4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ if test "$PHP_NEWRELIC" = "yes"; then
230230
fw_zend2.c fw_zend.c"
231231
LIBRARIES="lib_aws_sdk_php.c lib_monolog.c lib_doctrine2.c lib_guzzle3.c \
232232
lib_guzzle4.c lib_guzzle6.c lib_guzzle_common.c \
233-
lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c"
233+
lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c \
234+
lib_composer.c"
234235
PHP_NEW_EXTENSION(newrelic, $FRAMEWORKS $LIBRARIES $NEWRELIC_AGENT, $ext_shared,, \\$(NEWRELIC_CFLAGS))
235236

236237
PHP_SUBST(NEWRELIC_CFLAGS)

agent/fw_hooks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ extern void nr_phpunit_enable(TSRMLS_D);
5656
extern void nr_predis_enable(TSRMLS_D);
5757
extern void nr_zend_http_enable(TSRMLS_D);
5858
extern void nr_monolog_enable(TSRMLS_D);
59+
extern void nr_composer_handle_autoload(const char* filename);
5960

6061
/* Vulnerability Management Packages */
6162
extern void nr_drupal_version(void);

agent/lib_composer.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*
2+
* Copyright 2022 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "php_agent.h"
7+
#include "fw_hooks.h"
8+
#include "fw_support.h"
9+
#include "nr_txn.h"
10+
#include "util_logging.h"
11+
#include "util_memory.h"
12+
#include "util_syscalls.h"
13+
14+
static bool nr_execute_handle_autoload_composer_is_initialized() {
15+
zend_class_entry* zce = NULL;
16+
17+
if (NULL == (zce = nr_php_find_class("composer\\installedversions"))) {
18+
nrl_verbosedebug(NRL_INSTRUMENT,
19+
"Composer\\InstalledVersions class not found");
20+
return false;
21+
};
22+
23+
// the class is found - there's hope!
24+
if (NULL == nr_php_find_class_method(zce, "getinstalledpackages")
25+
|| NULL == nr_php_find_class_method(zce, "getversion")) {
26+
nrl_verbosedebug(
27+
NRL_INSTRUMENT,
28+
"Composer\\InstalledVersions class found, but methods not found");
29+
return false;
30+
}
31+
32+
return true;
33+
}
34+
35+
static int nr_execute_handle_autoload_composer_init(const char* vendor_path) {
36+
char* code = NULL;
37+
zval retval;
38+
int result = -1;
39+
40+
if (nr_execute_handle_autoload_composer_is_initialized()) {
41+
nrl_verbosedebug(NRL_INSTRUMENT, "%s: already initialized", __func__);
42+
return NR_SUCCESS;
43+
}
44+
45+
#if 0
46+
(void)vendor_path;
47+
code
48+
= nr_formatf(""
49+
"(function() {"
50+
" try {"
51+
" if (class_exists('Composer\\InstalledVersions')) {"
52+
" if (method_exists('Composer\\InstalledVersions', "
53+
" 'getInstalledPackages') && method_exists('Composer\\InstalledVersions', "
54+
" 'getVersion')) {"
55+
" return true;"
56+
" } else {"
57+
" return false;"
58+
" }"
59+
" } else {"
60+
" return false;"
61+
" }"
62+
" } catch (Exception $e) {"
63+
" return NULL;"
64+
" }"
65+
"})();");
66+
#else
67+
code = nr_formatf("include_once '%s/composer/InstalledVersions.php';",
68+
vendor_path);
69+
#endif
70+
71+
result = zend_eval_string(code, &retval, "newrelic\\init_composer_api");
72+
if (result != SUCCESS) {
73+
nrl_verbosedebug(NRL_INSTRUMENT,
74+
"%s: zend_eval_string(%s) failed, result=%d", __func__,
75+
code, result);
76+
return NR_FAILURE;
77+
}
78+
79+
zval_dtor(&retval);
80+
nr_free(code);
81+
82+
// Make sure runtime API is available after loading
83+
// Composer\\InstalledVersions class:
84+
if (!nr_execute_handle_autoload_composer_is_initialized()) {
85+
nrl_verbosedebug(NRL_INSTRUMENT,
86+
"%s: unable to initialize Composer runtime API", __func__);
87+
return NR_FAILURE;
88+
}
89+
90+
return NR_SUCCESS;
91+
}
92+
93+
static void nr_execute_handle_autoload_composer_get_packages_information(
94+
const char* vendor_path) {
95+
zval retval;
96+
int result = -1;
97+
98+
char* getpackagename
99+
= ""
100+
"(function() {"
101+
" try {"
102+
" return \\Composer\\InstalledVersions::getInstalledPackages();"
103+
" } catch (Exception $e) {"
104+
" return NULL;"
105+
" }"
106+
"})();";
107+
108+
char* getversion
109+
= ""
110+
"(function() {"
111+
" try {"
112+
" return \\Composer\\InstalledVersions::getVersion(\"%s\");"
113+
" } catch (Exception $e) {"
114+
" return NULL;"
115+
" }"
116+
"})();";
117+
118+
if (NR_SUCCESS != nr_execute_handle_autoload_composer_init(vendor_path)) {
119+
nrl_debug(NRL_INSTRUMENT,
120+
"%s - unable to initialize Composer runtime API - package info "
121+
"unavailable",
122+
__func__);
123+
return;
124+
}
125+
126+
nrl_verbosedebug(NRL_INSTRUMENT, "%s - Composer runtime API available",
127+
__func__);
128+
129+
#if 1
130+
result = zend_eval_string(getpackagename, &retval,
131+
"get installed packages by name" TSRMLS_CC);
132+
if (result == SUCCESS) {
133+
if (Z_TYPE(retval) == IS_ARRAY) {
134+
zval* value;
135+
char* buf;
136+
int result2;
137+
zval retval2;
138+
char* version = NULL;
139+
(void)version;
140+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL(retval), value) {
141+
if (Z_TYPE_P(value) == IS_STRING) {
142+
buf = nr_formatf(getversion, Z_STRVAL_P(value));
143+
result2 = zend_eval_string(buf, &retval2,
144+
"retrieve version for packages");
145+
nr_free(buf);
146+
if (SUCCESS == result2) {
147+
if (nr_php_is_zval_valid_string(&retval2)) {
148+
version = Z_STRVAL(retval2);
149+
}
150+
}
151+
}
152+
zval_dtor(&retval2);
153+
nrl_verbosedebug(NRL_INSTRUMENT, "package %s, version %s",
154+
NRSAFESTR(Z_STRVAL_P(value)), NRSAFESTR(version));
155+
if (NRINI(vulnerability_management_package_detection_enabled)) {
156+
nr_txn_add_php_package(NRPRG(txn), NRSAFESTR(Z_STRVAL_P(value)),
157+
NRSAFESTR(version));
158+
}
159+
nr_fw_support_add_package_supportability_metric(
160+
NRPRG(txn), NRSAFESTR(Z_STRVAL_P(value)), NRSAFESTR(version));
161+
}
162+
ZEND_HASH_FOREACH_END();
163+
} else {
164+
zval_dtor(&retval);
165+
return;
166+
}
167+
zval_dtor(&retval);
168+
}
169+
#else
170+
zv = nr_php_call(NULL, "Composer\\InstalledVersions::getInstalledPackages",
171+
NULL);
172+
if (NULL != zv) {
173+
char strbuf[NR_EXECUTE_DEBUG_STRBUFSZ];
174+
nr_format_zval_for_debug(zv, strbuf, 0, NR_EXECUTE_DEBUG_STRBUFSZ - 1, 0);
175+
nrl_always("Composer\\InstalledVersions::getInstalledPackages()=%s",
176+
strbuf);
177+
nr_php_zval_free(&zv);
178+
}
179+
#endif
180+
}
181+
182+
static char* nr_execute_handle_autoload_composer_get_vendor_path(
183+
const char* filename) {
184+
char* vendor_path = NULL; // result of dirname(filename)
185+
char* cp = NULL;
186+
187+
// vendor_path = dirname(filename):
188+
// 1. copy filename to vendor_path
189+
vendor_path = nr_strdup(filename);
190+
// 2. // find last occurence of '/' in vendor_path
191+
cp = nr_strrchr(vendor_path, '/');
192+
// 3. replace '/' with '\0' to get the directory path
193+
*cp = '\0';
194+
195+
return vendor_path;
196+
}
197+
198+
static bool nr_execute_handle_autoload_composer_file_exists(
199+
const char* vendor_path,
200+
const char* filename) {
201+
char* composer_magic_file = NULL; // vendor_path + filename
202+
bool file_exists = false;
203+
204+
composer_magic_file = nr_formatf("%s/%s", vendor_path, filename);
205+
if (0 == nr_access(composer_magic_file, F_OK | R_OK)) {
206+
file_exists = true;
207+
}
208+
nr_free(composer_magic_file);
209+
return file_exists;
210+
}
211+
212+
void nr_composer_handle_autoload(const char* filename) {
213+
// Composer signature file"
214+
#define COMPOSER_MAGIC_FILE_1 "composer/autoload_real.php"
215+
#define COMPOSER_MAGIC_FILE_1_LEN (sizeof(COMPOSER_MAGIC_FILE_1) - 1)
216+
// Composer runtime API files:
217+
#define COMPOSER_MAGIC_FILE_2 "composer/InstalledVersions.php"
218+
#define COMPOSER_MAGIC_FILE_2_LEN (sizeof(COMPOSER_MAGIC_FILE_2) - 1)
219+
#define COMPOSER_MAGIC_FILE_3 "composer/installed.php"
220+
#define COMPOSER_MAGIC_FILE_3_LEN (sizeof(COMPOSER_MAGIC_FILE_3) - 1)
221+
char* vendor_path = NULL; // result of dirname(filename)
222+
223+
vendor_path = nr_execute_handle_autoload_composer_get_vendor_path(filename);
224+
if (NULL == vendor_path) {
225+
nrl_verbosedebug(NRL_FRAMEWORK, "unable to get vendor path from '%s'",
226+
filename);
227+
return;
228+
}
229+
230+
if (!nr_execute_handle_autoload_composer_file_exists(vendor_path,
231+
COMPOSER_MAGIC_FILE_1)) {
232+
nrl_verbosedebug(NRL_FRAMEWORK, "'%s' not found in '%s'",
233+
COMPOSER_MAGIC_FILE_1, vendor_path);
234+
return;
235+
}
236+
237+
if (!nr_execute_handle_autoload_composer_file_exists(vendor_path,
238+
COMPOSER_MAGIC_FILE_2)) {
239+
nrl_verbosedebug(NRL_FRAMEWORK, "'%s' not found in '%s'",
240+
COMPOSER_MAGIC_FILE_2, vendor_path);
241+
return;
242+
}
243+
244+
if (!nr_execute_handle_autoload_composer_file_exists(vendor_path,
245+
COMPOSER_MAGIC_FILE_3)) {
246+
nrl_verbosedebug(NRL_FRAMEWORK, "'%s' not found in '%s'",
247+
COMPOSER_MAGIC_FILE_3, vendor_path);
248+
return;
249+
}
250+
251+
nrl_verbosedebug(NRL_FRAMEWORK, "detected composer");
252+
NRPRG(txn)->composer_info.composer_detected = true;
253+
nr_fw_support_add_library_supportability_metric(NRPRG(txn), "Composer");
254+
255+
nr_execute_handle_autoload_composer_get_packages_information(vendor_path);
256+
nr_free(vendor_path);
257+
}

0 commit comments

Comments
 (0)